最近在学Kotlin语法,学习到泛型章节发现有一些困难,于是回去复习了一下Java的泛型做一个对比学习,在此记录一下。
泛型
1. 概念
泛型(generics)是JDK5中引入的新特性,泛型提供了编译时类型安全的检测机制,该机制允许我们在编译时期检测到非法的数据结构类型。
//不做泛型的限制,ArrayList能存储多种数据结构类型。
ArrayList arrayList = new ArrayList();
arrayList.add("a");
arrayList.add(100);
arrayList.add(true);
//使用<T>限制ArrayList的接收类型,在编译时期就可以进行数据结构的检查。
ArrayList<String> stringArrayList = new ArrayList<>();
stringArrayList.add("a");
stringArrayList.add("b");
stringArrayList.add("c");
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(100);
integerArrayList.add(200);
integerArrayList.add(300);
泛型的本质就是将数据类型参数化,也就是所操作的数据类型被指定为一个参数。
2. 泛型类
1. 泛型类的定义
/**
* 泛型类的定义
* @param <T> 泛型标识--类型形参
* T 创建对象的时候再指定具体的参数类型
*/
public class Generic<T> {
//T是由于外部使用类时来指定
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "Generic{" +
"key=" + key +
'}';
}
}
2. 泛型类的使用
//泛型类在创建对象的时候,再来指定操作的具体数据类型。
Generic<String> stringGeneric = new Generic<>("a");
Generic<Integer> integerGeneric = new Generic<>(100);
//泛型类在创建对象的时候,没有指定类型,将按照object类型来接收。
Generic generic = new Generic("abc");
//泛型类不支持基本数据类型。
//Generic<int> intGeneric = new Generic<int>();
//同一泛型类,根据不同的数据类型创建的对象,本质上是同一个类型。
System.out.println(stringGeneric.getClass()); //Generic
System.out.println(integerGeneric.getClass()); //Generic
System.out.println(stringGeneric.getClass() == integerGeneric.getClass()); //true
3. Attention
- 泛型类,如果没有指定具体的数据类型,操作类型是Object
- 泛型的类型参数只能是类类型,不能是基本数据类型
- 泛型类型再逻辑上可以看成是多个不同的类型,但实际上都是相同类型
3. 泛型类派生子类
1. 子类也是泛型类,子类和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
2. 子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extend Generic<String>
4. 泛型类接口
1. 实现类不是泛型类,接口要明确数据类型
public class Apple implements Generator<String>
2. 实现类也是泛型类,实现类和接口的泛型类型要保持一致
public class Pair<T> implements Generator<T>
//泛型类的实现类的参数也可以添加泛型,但是必须包含父类泛型的类型。
public class Pair<T,E> implements Generator<T>
5. 泛型方法
1. 泛型方法能使方法独立于类而产生变化
/**
* 定义泛型方法
* @param list 参数
* @param <E> 泛型标识,具体类型由调用方法时指定。
* @return
*/
public <E> E getProduct(ArrayList<E> list) {
return list.get(random.nextInt(list.size()));
}
2. 如果static方法要使用泛型能力,就必须使其成为泛型方法
//这里使用了可变参数,表示能接收到多个泛型。
public static <E> void print(E... e) {
for (int i = 0; i < e.length; i++) {
System.out.println(e[i]);
}
}
6. 类型通配符
1. 类型通配符一般是使用 "?" 代替具体的类型实参
public static void showBox(Box<?> box) {
Object first = box.getFirst();
System.out.println(first);
}
public class Box<E> {
private E first;
public E getFirst() {
return first;
}
public void setFirst(E first) {
this.first = first;
}
}
使用 "?" 代替具体的参数,可以使方法可以接收不同的数据类型。
Box<Number> box1 = new Box<>();
box1.setFirst(100);
showBox(box1);
Box<Integer> box2 = new Box<>();
box2.setFirst(200);
showBox(box2);
2. 通配符是类型实参数,而不是类型形参
3. 类型通配符的上限
要求泛型的类型只能是实参类型,或实参类型的子类类型。
//类/接口<? extends 实参类型>
public static void showAnimal(List<? extends Cat> list)
4. 类型通配符的下限
要求泛型的类型只能是实参类型,或实参类型的父类类型。
//类/接口<? super 实参类型>
public static void showAnimal(List<? super Cat> list)
7. 类型擦除
泛型是JDK5才引入的概念,在这之前是没有泛型的,但是,泛型代码却能够很好的和之前版本的代码兼容。那是因为,泛型信息仅存在于代码编译的阶段,在进入JVM之前,与泛型相关的信息会被擦除,称之为——类型擦除。
1. 无限制类型擦除
将 擦除成 Object
public class Erasure<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
//类型擦除
public class Erasure {
private Object key;
public Object getKey() {
return key;
}
public void setKey(Object key) {
this.key = key;
}
}
2. 有限制类型擦除
将 <T extends 父类> 擦除成父类
public class Erasure<T extends Number> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
//类型擦除
public class Erasure {
private Number key;
public Number getKey() {
return key;
}
public void setKey(Number key) {
this.key = key;
}
}
3. 擦除方法中类型定义的参数
将 <T extends 父类> 擦除成父类
public <T extends List> T show(T t)
//类型擦除
public Number show(Number t)
4. 桥接方法
实现泛型接口的时,编译器会生成桥接接口。
//泛型接口
public interface Info<T> {
T info (T t);
}
//泛型接口的实现类
public class InfoImpl implements Info<Integer> {
@Override
public Integer info(Integer value) {
rerurn value;
}
}
//类型擦除
public interface Info{
Object info(Object t);
}
public class InfoImpl implements Info {
public Integer info(Integer value) {
return value;
}
//桥接方法,保持接口和类的实现关系。
@Override
public Object info(Object var) {
return info((Integer) var);
}
}