1) 如果你定义了一个泛型(类、接口),那么Java规定,你不能在所有的静态方法、静态初块等所有静态内容中使用泛型的类型参数。例如:
public class A<T> {
publicstaticvoidfunc(T t) {
//报错,编译不通过
}
}
(2) 如何在静态内容(静态方法)中使用泛型,更一般的问题是,如果类(或者接口)没有定义成泛型,但是就想在其中某几个方法中运用泛型(比如接受一个泛型的参数等),该如何解决?
- 定义泛型方法就像定义泛型类或接口一样,在定义类名(或者接口名)的时候需要指定我的作用域中谁是泛型参数。例如:
public class A<T> { ... }
表明在类A的作用域中,T是泛型类型参数。 - 定义泛型方法,其格式是:修饰符 <类型参数列表> 返回类型 方法名(形参列表) { 方法体 }。例如:
public static <T, S> int func(List<T> list, Map<Integer, S> map) { ... }
,其中T和S是泛型类型参数。 - 泛型方法的定义和普通方法定义不同的地方在于需要在修饰符和返回类型之间加一个泛型类型参数的声明,表明在这个方法作用域中谁才是泛型类型参数;
- 不管是普通的类/接口的泛型定义,还是方法的泛型定义都逃不出两大要素:
- 明哪些是泛型类型参数;
- 这些类型参数在哪里使用。
(3) 类型参数的作用域
-
class A<T> { ... }
中T的作用域就是整个A; -
public <T> func(...) { ... }
中T的作用域就是方法func; -
类型参数也存在作用域覆盖的问题,可以在一个泛型模板类/接口中继续定义泛型方法,例如:
class A<T> {
// A已经是一个泛型类,其类型参数是T
public static <T> voidfunc(T t) {
// 再在其中定义一个泛型方法,该方法的类型参数也是T
}
}
//当上述两个类型参数冲突时,在方法中,方法的T会覆盖类的T,即和普通变量的作用域一样,内部覆盖外部,外部的同名变量是不可见的。
//除非是一些特殊需求,一定要将局部类型参数和外部类型参数区分开来,避免发生不必要的错误,因此一般正确的定义方式是这样的:
class A<T> {
public static <S> voidfunc(S s) {
}
}
(4) 泛型方法的类型参数可以指定上限,类型上限必须在类型参数声明的地方定义上限,不能在方法参数中定义上限。规定了上限就只能在规定范围内指定类型实参,超出这个范围就会直接编译报错。
-
<T extends X> void func(List<T> list){ ... }
,正确 -
<T extends X> void func(T t){ ... }
,正确 -
<T> void func(List<T extends X> list){ ... }
,编译错误
2. 泛型调用
(1) 显式指定方法的类型参数,类型参数要写在尖括号中并放在方法名之前。例如:object.<String> func(...)
,这样就显式指定了泛型方法的类型参数为String,那么所有出现类型参数T的地方都将替换成String类型。
(2) 隐式地自动推断,不指明泛型参数,编译器根据传入的实参类型自动推断类型参数。例如:<T> void func(T t){ ... }
隐式调用object.func("name")
,根据"name"
的类型String推断出类型参数T的类型是String
(3) 避免歧义,例如:<T> void func(T t1, T t2){ ... }
如果这样调用的话object.func("name", 15);
虽然编译不会报错,但是仍然会有很大隐患,T到底应该是String还是Integer存在歧义;
(4) 有些歧义Java是会直接当成编译错误的,即所有和泛型参数有关的歧义,例如:<T> void func(List<T> l1, List<T> l2){...}
如果这样调用的话,object.func(new List<String>(), new List<Integer>());
这里会有歧义,编译器无法知道T到底应该是String还是Integer,这种歧义会直接报错的,编译无法通过。即泛型方法中,如果类型参数刚好就是泛型参数的类型实参,那么这个类型实参不得有歧义,否则直接编译报错。
3. 泛型方法/类型通配符
(1) 你会发现所有能用类型通配符(?)解决的问题都能用泛型方法解决,并且泛型方法可以解决的更好。
- 类型通配符:
void func(List<? extends A> list);
- 完全可以用泛型方法完美解决:
<T extends A> void func(List<T> list);
(2) 两种方法可以达到相同的效果,“?”可以代表范围内任意类型,而T也可以传入范围内的任意类型实参,并且泛型方法更进一步,“?”泛型对象是只读的,而泛型方法里的泛型对象是可修改的,即List<T> list
中的list是可修改的。
(3) 两者最明显的区别
- “?”泛型对象是只读的,不可修改,因为“?”类型是不确定的,可以代表范围内任意类型;
- 而泛型方法中的泛型参数对象是可修改的,因为类型参数T是确定的(在调用方法时确定),因为T可以用范围内任意类型指定;
(3) 适用场景
- 一般只读就用“?”,要修改就用泛型方法。例如:
public <T> voidfunc(List<T> list, T t) {
list.add(t);
}
- 在多个参数、返回值之间存在类型依赖关系就应该使用泛型方法,否则就应该是通配符“?”。具体就是,如果一个方法的返回值、某些参数的类型依赖另一个参数的类型就应该使用泛型方法,因为被依赖的类型如果是不确定的"?",那么其他元素就无法依赖它。例如:
<T> void func(List<? extends T> list, T t);
即第一个参数依赖第二个参数的类型(第一个参数list的类型参数必须是第二个参数的类型或者其子类)。 -
<T, E extends T> void func(List<T> l1, List<E> l2);
这里E只在形参中出现了一次(类型参数声明不算),并且没有任何其他东西(方法形参、返回值)依赖它,那么就可以把E规约成“?”。规约结果<T> void func(List<T> l1, List<? extends T> l2);
- 典型应用,容器赋值方法(Java的API):
public static <T> void Collections.copy(List<T> dest, List<? extends T> src) { ... }
从src拷贝到dest,那么dest最好是src的类型或者其父类,因为这样才能类型兼容,并且src只是读取,没必要做修改,因此使用“?”还可以强制避免对src做不必要的修改,增加的安全性。
相关推荐
关于java基础的泛型的练习
【Java基础】泛型方法 - 右撇子 - 博客频道 - CSDN.NET
java基础-泛型通配符
下面是那种典型用法: List myIntList = new ArrayList();// 1 myIntList.add(new Integer(0));// 2 Integer x = (Integer) myIntList.iterator().next();// 3 第3 行的类型转换有些烦人。通常情况下,程序员知道一...
大学课程讲义_Java基础_泛型.docx
集合框架及泛型的介绍和基础理解,方便大家了解集合框架及泛型。
java零基础自学 之 JAVA泛型 java零基础自学 之 JAVA泛型
自己整理的JAVA学习笔记,非计算机专业,包括数据基础和泛型编程,集合,多线程,IO流,网络部分未上传,如果觉得对你有帮助就很棒啦!
详细的介绍了集合框架的用法,及其语法规则,剖析了使用的使用注意事项,帮助更牢靠的掌握集合框架的知识及泛型内容。谢谢
Java基础入门(四)-泛型、反射、注解
思维导图
Java 课件 (包括基础语法,数组与语句,面向对象编程,java异常处理,java常用基础类,java集合与泛型)
java基础泛型 学习全文件
该文档主要包括为什么使用泛型、在集合中使用泛型、自定义泛型结构、泛型在集成上的体现以及通配符的使用等内容
Java泛型概念 Java泛型是一种在编译时进行类型检查和类型推断的机制,它可以让我们编写更加通用、可重用的代码,提高了代码的可读性和可维护性,同时保证了类型安全。 Java泛型的核心思想是类型参数化,即在类、接口...
本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...
这是我在网络上学习所收获到的一些心得,想和大家分享一下。如有侵权或者错误请大家私信我,我会进行删除与修改。
学士后Java集合框架和泛型课后习题答案 希望能够帮助大家更好的学习JAVA,这些知识还是比较基础的,并没有多大难度 一起共同进步
Java SE编程入门教程 java泛型(共11页).pptx Java SE编程入门教程 java封装继承多态(共56页).pptx Java SE编程入门教程 java集合(共38页).pptx Java SE编程入门教程 java接口(共21页).pptx Java SE编程入门...
有一定Java编程基础的开发者;对泛型概念感兴趣或希望加深理解的学习者。 使用场景: 学习和理解Java泛型的基本概念和语法; 实际项目中需要使用泛型来增加类型安全性和重用性的开发任务。 目标: 本代码资源的...