一、概念
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
1. 泛型方法
具有一个或多个类型变量的方法,称之为泛型方法。类型变量也叫类型参数。
语法:
public static < E > void printArray( E[] e ); public static <T extends Comparable<T>> T maximum(T x, T y, T z);//有界(上届)的类型参数
规则:
(1) 返回值之前类型变量声明;
(2) 多个类型参数声明时用逗号隔开;
(3) 泛型方法可以存在于非泛型类中;
(4) 类型参数能被用来声明返回值类型;
注意:类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
有界的类型参数:
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
一个操作数字的方法可能只希望接受Number或者Number子类的实例.
2. 泛型类
泛型类需要在类名后面添加了类型参数声明部分.
语法:
public class Box<T>;
3. 类型通配符
(1) 类型通配符一般是使用?代替具体的类型参数。
语法:
public static void getData(List<?> data); // 上届:通配符泛型只接受Number及其下层子类类型 public static void getUperNumber(List<? extends Number> data);
有界的类型参数:
<? extends T>表示该通配符所代表的类型是T类型的子类。 [上限]
<? super T>表示该通配符所代表的类型是T类型的父类。 [下限]
4. 常用通配符
常用以下字符表示参数类型:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd、3rd、4th types
5. T和?的区别
T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
区别1:通过 T 来 确保 泛型参数的一致性
// 通过 T 来 确保 泛型参数的一致性 public <T extends Number> void test(List<T> dest, List<T> src) //通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型 public void test(List<? extends Number> dest, List<? extends Number> src)
区别2:类型参数可以多重限定而通配符不行
使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。
区别3:通配符可以使用超类限定而类型参数不行
// 类型参数 T 只具有 一种 类型限定方式: T extends A // 但是通配符 ? 可以进行 两种限定: ? extends A ? super A
6.Class<T> 和 Class<?> 区别
Class<T>和 Class<?> 最常见的是在反射场景下的使用。
// 通过反射的方式生成 multiLimit 对象,这里比较明显的是,我们需要使用强制类型转换 MultiLimit multiLimit = (MultiLimit)Class.forName("com.atguigu.MultiLimit").newInstance();
对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。
对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接检查到类型的问题:
public class Test { public static <T> T getInstance(Class<T> cls) throws Exception { return cls.newInstance(); } public static void main(String[] args) throws Exception { A a = getInstance(A.class); B b = getInstance(B.class); } } class A {} class B {}
Class<T>
在实例化的时候,T 要替换成具体类。Class<?>
它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明:
public class Test2<T> { // 可以 public Class<?> clazz; // 不可以,因为 T 需要指定类型 public Class<T> clazzT; }
所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class。
那如果也想 public Class<T> clazzT;
这样的话,就必须让当前的类也指定 T ,
public class Test2<T> { // 可以 public Class<?> clazz; // 不可以,因为 T 需要指定类型 public Class<T> clazzT; }
二、使用教程
1. 泛型方法
// 泛型方法 printArray public static <E> void printArray(E[] inputArray) { // 输出数组元素 for (E element : inputArray) { System.out.printf("%s ", element); } }
有界的泛型方法:
// 比较三个值并返回最大值 参数的类型必须实现了Comparable接口,即必须是可比较的 public static <T extends Comparable<T>> T maximum(T x, T y, T z) { T max = x; // 假设x是初始最大值 if (y.compareTo(max) > 0) { max = y; //y 更大 } if (z.compareTo(max) > 0) { max = z; // 现在 z 更大 } return max; // 返回最大对象 }
2. 泛型类
public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<String> stringBox = new Box<String>(); stringBox.add(new String("陈数")); // 是换行符,使光标定位到下一行 System.out.printf("字符串为 :%s ", stringBox.get()); } }
3. 类型通配符
public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); }
有上界的类型通配符:
public static void getUperNumber(List<? extends Number> data) { System.out.println("data :" + data.get(0)); }
参考: