java 泛型
1.泛型简介
使用泛型,可以限制集合能接收的对象类型,并且消除了类型转换的需要
List<T> list=new ArrayList<T>(); //使用泛型
T t=list.get(0);
List list=new ArrayList(); //不使用泛型
T t=(T)list.get(0);
相关术语:(以ArrayList<E>、ArrayList<Integer>为例)
- ArrayList<E>定义了一个泛型类型,“E”称为 “类型变量”或“类型参数”。
- ArrayList<Integer>称为“参数化的类型”,“Integer”称为“实际类型参数”。
- ArrayList称为泛型类型ArrayList<E>的“原始类型(raw type)”。
泛型中标记含义:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的java类型
- S、U、V - 2nd、3rd、4th types
通配符对应类名,类对应变量名。可以这样做个对比
一般使用<T>来声明类型持有者名称,自定义泛型类时,类持有者名称可以使用T(Type)如果是容器的元素可以使用E(Element),若键值匹配可以用K(Key)和V(Value)等,若是<?>,则是默认是允许Object及其下的子类,也就是java的所有对象了
2.泛型类
实例:
class Pair<T>{
private T first;
private T second;
public Pair(){
setFirst(null);
setSecond(null);
}
public Pair(T f,T s){
this.setFirst(f);
this.setSecond(s);
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
}
实例化:使用时,必须给T指定一个具体的类型(比如String):Pair<String>
注意:
- 泛型参数必须是引用类型,不适用于基本数据类型
- 不能定义泛型化数组 Pair<String>[] table=new Pair<String>[10];是错的
- 不能直接创建泛型类型的实例
public Pair() {
first=new T();
second=new T();
}//是错误的
- 泛型类型不能直接或间接继承自Throwable,也无法抛出或捕获泛型类型的异常对象
- 不能定义静态泛型成员
从泛型类派生子类:
不允许基类中有泛型参数
class Child extends MyClass<T>{} //是错误的
class Child extends MyClass<String>{}//对的
如果MyClass是泛型类,在定义子类时不指定泛型参数,则MyClass的泛型参数默认为Object
3.泛型方法
可以在普通类或泛型类中定义泛型方法:
public class TestMain {
public static void main(String[] args) {
String[] names={"aaa","bbb","ccc"};
String mid=ArrayUtils.<String>getMiddle(names);
//上句等同于
mid=ArrayUtils.getMiddle(names);
}
}
class ArrayUtils{
public static <T> T getMiddle(T[] arr){
return arr[arr.length/2];
}
}
<T> T 可以类比 T\[\] arr
可变参数,使用…定义个数可变参数:
public static <T> List<T> toList(T... arr){
List<T> list=new ArrayList<T>();
for(T o:arr) list.add(o);
return list;
}
4.泛型多态特性
public class TestMain {
public static void main(String[] args) {
List<Number> nums=new ArrayList<Number>();
nums.add(2);
nums.add(3.14);
for (Number number : nums) {
System.out.println(number.getClass().getName());
}
// List<Integer> ints=new ArrayList<Integer>();
// ints.add(1);
// ints.add(2);
// List<Number> nums2=ints;//报错
}
}
以上例子总结:
List<Number> ArrayList<Number>是父子类关系
List<Number> List<Integer>不是父子俩关系!!!
5.泛型约束
有的时候,我们希望对泛型参数进行一些限制,比如,希望它实现XXX接口,必须是XXX的子类等等
指定泛型方法的泛型参数类型必须实现Comparable接口
public static <T extends Comparable> T min(T[] a){
//
}
还可以指定多个约束条件:
T extends Comparable & Serializable
注意:多个约束中最多只能是一个类,且放在第一位
参数约束实例:
public class TestMain <T extends Number>{
public static void main(String[] args) {
TestMain<Integer> ai=new TestMain<Integer>();//OK
//TestMain<String> as=new TestMain<String>();//错误
}
}
JDK中”?”真实事例:
interface Collection<E> {
public boolean addAll(Collection< ? extends E > c);
...
}
故以下代码正确:
List<Number> nums = new ArrayList<Number>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> dbls = Arrays.asList(2.78, 3.14);
nums.addAll(ints);
nums.addAll(dbls);
<? extends T>表示该通配符所代表的类型是T类型的子类。
<? super T>表示该通配符所代表的类型是T类型的父类。
6.泛型类型擦除原理
public class Pair<T>{
private T first;
private T second;
}
对上述代码,Java编译器会将上述的T转为Object,以便在JVM上运行。——这就是擦除(erasure)
原因:Java虚拟机不直接支持泛型类型
泛型方法翻译实例:
public static <T extends Comparable> T min(T[] a)
擦除后:
public static Comparable Min(Comparable[] a)
也就是说实际并没有泛型这么一个东西。。。JVM在擦除后生成类型转换检查指令来匹配通配符。
参考:
金旭亮Java编程系列(大部分)