2、将运行期的类型转换异常提前到了编译期,保证类型的安全,避免类型转换异常
3、怎么去定义和使用泛型?
我们可以给一个类,方法,或者接口指定泛型,在具体使用的地方指定具体的类型
一、Java 泛型
要学习好 Kotlin 泛型,我们先要对 Java 泛型足够的了解,因为 Kotlin 泛型和 Java 泛型基本上是一样的,只不过在 Kotlin 上有些东西换了新的写法
1、泛型的简单使用
在 Java 中,我们可以给一个类,方法,或者接口指定泛型,在具体使用的地方指定具体的类型
1)、定义一个泛型类,在类名的后面加上 <T>
这种语法结构就是定义一个泛型类,泛型可以有任意多个
//定义一个泛型类
public class JavaGenericClass<T> {
private T a;
public JavaGenericClass(T a) {
this.a = a;
}
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
//泛型类使用
public static void main(String[] args) {
//编译器可推断泛型类型,因此 new 对象后面的泛型类型可省略
JavaGenericClass<String> javaGenericClass1 = new JavaGenericClass<String>("erdai");
JavaGenericClass<Integer> javaGenericClass2 = new JavaGenericClass<>(666);
System.out.println(javaGenericClass1.getA());
System.out.println(javaGenericClass2.getA());
}
}
//打印结果
erdai
666
2)、定义一个泛型方法,在方法的返回值前面加上 <T>
这种语法结构就是定义一个泛型方法,泛型可以有任意多个,泛型方法的泛型与它所在的类没有任何关系
public class JavaGenericMethod {
public <T> void getName(T t){
System.out.println(t.getClass().getSimpleName());
}
public static void main(String[] args) {
JavaGenericMethod javaGenericMethod = new JavaGenericMethod();
//编译器可推断出泛型类型,因此这里的泛型类型也可省略
javaGenericMethod.<String>getName("erdai666");
}
}
//打印结果
String
3)、定义一个泛型接口
在接口名的后面加上 <T>
这种语法结构就是定义一个泛型接口,泛型可以有任意多个
public interface JavaGenericInterface<T> {
T get();
}
class TestClass<T> implements JavaGenericInterface<T>{
private final T t;
public TestClass(T t) {
this.t = t;
}
@Override
public T get() {
return t;
}
}
class Client{
public static void main(String[] args) {
JavaGenericInterface<String> javaGenericInterface = new TestClass<>("erdai666");
System.out.println(javaGenericInterface.get());
}
}
//打印结果
erdai666
2、泛型擦除
1、泛型擦除是什么?
看下面这段代码:
//使用了不同的泛型类型 结果得到了相同的数据类型
public class JavaGenericWipe {
public static void main(String[] args) {
Class a = new ArrayList<String>().getClass();
Class b = new ArrayList<Integer>().getClass();
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("a == b: " + (a == b));
}
}
//打印结果
a = class java.util.ArrayList
b = class java.util.ArrayList
a == b: true
为啥会出现这种情况呢?
因为 Java 中的泛型是使用擦除技术来实现的:泛型擦除是指通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上
之所以要使用泛型擦除是为了兼容 JDK 1.5 之前运行时的类加载器,避免因为引入泛型而导致运行时创建不必要的类
2、泛型擦除的具体步骤
1)、擦除所有类型参数信息,如果类型参数是有界的,则将每个参数替换为其第一个边界;如果类型参数是无界的,则将其替换为 Object类型擦除的规则:
<T>
擦除后变为 Object
<T extends A>
擦除后变为 A
<? extends A>
擦除后变为 A
<? super A>
擦除后变为Object
2)、(必要时)插入类型转换,以保持类型安全
3)、(必要时)生成桥接方法以在子类中保留多态性
//情况1: 擦除所有类型参数信息,如果类型参数是有界的,则将每个参数替换为其第一个边界;如果类型参数是无界的,则将其替换为 Object
class Paint {
void draw() {
System.out.println("Paint.draw() called");
}
}
//如果不给 T 设置边界,那么 work 方法里面的 t 就调用不到 draw 方法
class Painter<T extends Paint> {
private T t;
public Painter(T t) {
this.t = t;
}
public void work() {
t.draw();
}
}
//情况2:(必要时)插入类型转换,以保持类型安全
public class JavaGenericWipe {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("erdai");
stringList.add("666");
for (String s : stringList) {
System.out.println(s);
}
}
}
//编译时生成的字节码文件翻译过来大致如下
public class JavaGenericWipe {
public JavaGenericWipe() {