常见问题

  1. List 和 List<?> 的区别?
    1. 讨论“<T>"和"<?>",首先要区分开两种不同的场景:
    2. 第一,声明一个泛型类或泛型方法。
    3. 第二,使用泛型类或泛型方法。


List<?>和List的区别? - 知乎

  1. 和 的区别 ``` 上界<? extends T>不能往里存,只能往外取 下界<? super T>不影响往里存,但往外取只能放在Object对象里 PECS原则 PECS(Product Extend Consumer Super)原则: 1.频繁往外读取,适合使用上界Extends 2.经常往里插入,适合使用下界Super

A extend B 表示A是B的子类或者子孙,在B下面。上界通配符 < ? extends E> A super B 表示A是B的父类或者祖先,在B的上面。

  1. <br />[Java 泛型 <? super T> super 怎么 理解?与 extends 有何不同? - 知乎](https://www.zhihu.com/question/20400700/answer/117464182)
  2. 3.
  3. Class Class<?> 区别

Class 在实例化的时候,T 要替换成具体类。Class<?> 它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。

  1. <a name="df368884"></a>
  2. ## 前言
  3. Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。
  4. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
  5. <a name="02987f86"></a>
  6. ## 泛型带来的好处
  7. 在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。
  8. 那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。
  9. <a name="409af731"></a>
  10. ## 泛型的使用
  11. 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
  12. <a name="6551862e"></a>
  13. ### 泛型类
  14. 泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
  15. 泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解):
  16. ```java
  17. class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  18. private 泛型标识 /*(成员变量类型)*/ var;
  19. .....
  20. }
  21. }

一个最普通的泛型类:

  1. //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
  2. //在实例化泛型类时,必须指定T的具体类型
  3. public class Generic<T>{
  4. //key这个成员变量的类型为T,T的类型由外部指定
  5. private T key;
  6. public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
  7. this.key = key;
  8. }
  9. public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
  10. return key;
  11. }
  12. }
  13. //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
  14. //传入的实参类型需与泛型的类型参数类型相同,即为Integer.
  15. Generic<Integer> genericInteger = new Generic<Integer>(123456);
  16. //传入的实参类型需与泛型的类型参数类型相同,即为String.
  17. Generic<String> genericString = new Generic<String>("key_vlaue");
  18. Log.d("泛型测试","key is " + genericInteger.getKey());
  19. Log.d("泛型测试","key is " + genericString.getKey());
  20. 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is 123456
  21. 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is key_vlaue
  • 泛型的类型参数只能是类类型,不能是简单类型。
  • 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
  1. if(ex_num instanceof Generic<Number>){
  2. }

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:

  1. //定义一个泛型接口
  2. public interface Generator<T> {
  3. public T next();
  4. }

当实现泛型接口的类,未传入泛型实参时:

  1. /**
  2. * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
  3. * 即:class FruitGenerator<T> implements Generator<T>{
  4. * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
  5. */
  6. class FruitGenerator<T> implements Generator<T>{
  7. @Override
  8. public T next() {
  9. return null;
  10. }
  11. }

当实现泛型接口的类,传入泛型实参时:

  1. /**
  2. * 传入泛型实参时:
  3. * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
  4. * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
  5. * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
  6. * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
  7. */
  8. public class FruitGenerator implements Generator<String> {
  9. private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
  10. @Override
  11. public String next() {
  12. Random rand = new Random();
  13. return fruits[rand.nextInt(3)];
  14. }
  15. }

泛型方法

在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。

尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。

泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型

  1. /**
  2. * 泛型方法的基本介绍
  3. * @param tClass 传入的泛型实参
  4. * @return T 返回值为T类型
  5. * 说明:
  6. * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
  7. * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  8. * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  9. * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
  10. */
  11. public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
  12. IllegalAccessException{
  13. T instance = tClass.newInstance();
  14. return instance;
  15. }
  16. Object obj = genericMethod(Class.forName("com.test.test"));

泛型通配符

类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。

可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

参考

Java泛型中的通配符 T,E,K,V,?,你确定都了解吗?

java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一 - s10461的博客 - CSDN博客