Java中的泛型
作用: 是将运行时的异常转到编译时来处理,代码更健壮,更简洁,更灵活,复用性强
AIShoes
AIShoes
AIShoes
AIShoes
用法: 作用在接口之上,比如一个鞋子接口
public interface IShoes {
}
作用在方法之上
public AIShoes getShoes(){
return new AIShoes();
}
作用在类之上
public class AIShoes {
T t;
@Override
public void setShoesType(T t) {
this.t = t;
}
@Override
public T getShoesType() {
return t;
}
}
泛型的擦除
原理: 泛型是JDK5引入的,虚拟机其实是不支持泛型的,所以java实现的是一种伪泛型机制. 为了向下兼容,虚拟机在编译期就会做擦除泛型的操作,这样java就不需要产生新的字节码,所以在java运行时根本就不存在泛型的信息.
泛型是如何擦除的
当一个泛型类编译成字解码后,会将这个
就拿上面的例子来说
public class AIShoes implements IShoes {
T t;
@Override
public void setShoesType(T t) {
this.t = t;
}
@Override
public T getShoesType() {
return t;
}
}
这个类,使用AMS工具查看擦除后是什么样的
// class version 51.0 (51)
// access flags 0x21
// signature Ljava/lang/Object;Lcom/ancely/fanxing/demo2/IShoes;
// declaration: com/ancely/fanxing/demo2/AIShoes implements com.ancely.fanxing.demo2.IShoes
public class com/ancely/fanxing/demo2/AIShoes implements com/ancely/fanxing/demo2/IShoes {
// compiled from: AIShoes.java
// access flags 0x0
// signature TT;
// declaration: T
Ljava/lang/Object; t
// access flags 0x1
public ()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object. ()V
RETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/AIShoes;
// declaration: com.ancely.fanxing.demo2.AIShoes
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
// signature (TT;)V
// declaration: void setShoesType(T)
public setShoesType(Ljava/lang/Object;)V
L0
LINENUMBER 8 L0
ALOAD 0
ALOAD 1
PUTFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
L1
LINENUMBER 9 L1
RETURN
L2
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L2 0
// signature Lcom/ancely/fanxing/demo2/AIShoes;
// declaration: com.ancely.fanxing.demo2.AIShoes
LOCALVARIABLE t Ljava/lang/Object; L0 L2 1
// signature TT;
// declaration: T
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1
// signature ()TT;
// declaration: T getShoesType()
public getShoesType()Ljava/lang/Object;
L0
LINENUMBER 13 L0
ALOAD 0
GETFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
ARETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/AIShoes;
// declaration: com.ancely.fanxing.demo2.AIShoes
MAXSTACK = 1
MAXLOCALS = 1
}
很明显,将T 改成了Object
再来看下面的例子,使用的是T extends Shoes>
public class NikeShoes {
public void setShoesType(T t) {
}
public T getShoesType() {
return null;
}
}
编译后
// class version 51.0 (51)
// access flags 0x21
// signature Ljava/lang/Object;
// declaration: com/ancely/fanxing/demo2/NikeShoes
public class com/ancely/fanxing/demo2/NikeShoes {
// compiled from: NikeShoes.java
// access flags 0x1
public ()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object. ()V
RETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/NikeShoes;
// declaration: com.ancely.fanxing.demo2.NikeShoes
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
// signature (TT;)V
// declaration: void setShoesType(T)
public setShoesType(Lcom/ancely/fanxing/demo2/Shoes;)V
L0
LINENUMBER 6 L0
RETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/NikeShoes;
// declaration: com.ancely.fanxing.demo2.NikeShoes
LOCALVARIABLE t Lcom/ancely/fanxing/demo2/Shoes; L0 L1 1
// signature TT;
// declaration: T
MAXSTACK = 0
MAXLOCALS = 2
// access flags 0x1
// signature ()TT;
// declaration: T getShoesType()
public getShoesType()Lcom/ancely/fanxing/demo2/Shoes;
L0
LINENUMBER 9 L0
ACONST_NULL
ARETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/NikeShoes;
// declaration: com.ancely.fanxing.demo2.NikeShoes
MAXSTACK = 1
MAXLOCALS = 1
}
很明显擦除后,这个T变成了Shoes了
总结: 擦除总共做了三件事; 1: 判断泛型有没有限制,没有限制则转为Object. 2: 如果有限制则转为相对应的类型; 3:有限制并实现了一个接口的话,会
public synthetic bridge setShoesType(Ljava/lang/Object;)V
L0
LINENUMBER 3 L0
ALOAD 0
ALOAD 1
CHECKCAST com/ancely/fanxing/demo2/Shoes//这句是强转的意思
INVOKEVIRTUAL com/ancely/fanxing/demo2/NikeShoes.setShoesType //这句是调用了setShoesType方法(Lcom/ancely/fanxing/demo2/Shoes;)V
RETURN
L1
LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
// signature Lcom/ancely/fanxing/demo2/NikeShoes;
// declaration: com.ancely.fanxing.demo2.NikeShoes
MAXSTACK = 2
MAXLOCALS = 2
}
多生成一个bridge方法在这个bridge方法中再调用相应的set方法.
为什么会多了一个set方法呢,是因为一个接口在擦除泛型的时候,泛型没有限制的时候会变成Object,而一个类去实现这个接口的时候,因为这个类的泛型有限制,所以在擦除时不会是Object类型, 而在实现接口又一定要实现里面的方法,所以需要一个Object类型的参数,所以就会多出一个带Object参数的bridge方法.
还有一点,擦除完了的类会保存在类的常量池中,所以我们可以通过相应API获取到泛型的类型.(File.getGenericType).
泛型的进阶使用
public static void appendS(List list){
T t = new T();
list.add(t);
}
比如上面这个泛型方法,这样在代码里面肯定是报错的,有什么方法可以在里面直接new一个T出来呢,就可以用下面这种方法
public static void append(List list,Class clazz){
R r = null;
try {
r = clazz.newInstance();
list.add(r);
} catch (Exception e) {
e.printStackTrace();
}
}
面试题说法:
泛型是为了把运行时的异常移到编译时来处理,它还能使代码更具健壮,灵活,简洁,复用性,它是jdk1.5引入的,jvm虚拟机是不支持泛型的,所以为了兼容,编译的时候就会做泛型擦除的操作.如果泛型没有任何通配符限制的话最终都是转成Object.而通配符又分为非限定通配符和限定通配符 ,非限定的话是用一个? 代表, 限定通配符又分为二种,一个管上限 ? super T; 一个管下限 ? extends T .
? super T 代表的是T 和他的父类及以上 它可以存,不能取,可以称为消费者
? extends T 则代表的是T 和T的子类及以下,它只能取数据不能存,可以称为生产者
打个比放 E extends D D extends C C extends B B extends A
那么List extends B> list1 ; 这个list1它只能获取数据不能添加数据, 为什么称为和产者, 是因为他可以不断的获取数据给消费者用.
而List super B> list2; 这个list2他可以添加数据,但是他不能取数据,为什么称为消费者,是因为他拿到数据就添加
比如Collections里面的copy方法他的前二个参数: 第一个为super, 第二个extends