Java泛型机制

来源:互联网 发布:linux mysql 编辑:程序博客网 时间:2024/05/21 19:26

JAVA的泛型其实是个编写和编译期的把戏~~
很多地方只是将这个泛型定为语法糖。
但这并不是说JAVA的泛型不是一个好东西。我相信Sun是有能力实现真正泛型的。

他们之所以使用这套泛型是为了:兼容1.5之前的类库。

JAVA 之所以这个泛型 是做到了:

IDE编写使用时的静态类型推导、静态类型检查、 实际类型填充(仅在逻辑上)编译后程序中所有的泛型类型将被擦除,替换为他们的非泛型上界。运行时反射获得的参数类型为擦除后的类型。仅有继承后有实际类型填充的才可以获得实际参数类型

Java泛型 告诉编译器想使用什么类型,编译器帮你处理所有细节(检查类型 获得转型 生成桥接方法等)

擦除到第一个类型(擦除到第一个边界):<T extends Person> <T extends Object><? Extends T>

编写方式

  • 方法泛型
Public <F,T> F convert(T t);
  • 通用泛型 [容器 承载对象 和对象具体逻辑无关]
Class Demo<T>{Private T x;Public T get(){Return x;}Public void set(T x){This. X= x;}}
  • 界限泛型 [承载对象 并使用对象方法]

    <T extends Person> 有界限以后就可以将T绑定到具体的方法了。因为运行时的T已被擦除到上界 Person了

Class Demo<T extends Person >{Private T x;Public void say(String words){ x. say (words);}}
  • 创建泛型 [需要创建 ]
外部工厂Class Demo<T>{Private T x;Public <F extends Factory<T>> Demo(F factory){X = factory.create();}}模板方法Abstract class GenericWithCreate<T>{Final T element;GenericWithCreate(){element  = create()}Abstract T create();}
  • 有界通配符
实际引用参数<? Extends Person><? Super Person>编写泛型类或方法<? Extends T>      Class H<? Extends T> 通配有界泛型类List<Apple> list;List<? Extends Fruit> flist;Flist = list;
  • 异常泛型
Interface process<T,E extends Exception>{Void process(List<T> resultCollector throws E);}

编译方式

为了探索泛型在编译时的变化。
准备了一个例子:

public interface Factory<T,F> {     T create(F f);}
public class MemiList<T> {    private T t;    public T getT() {        return t;    }    public void setT(T t) {        this.t = t;    }}
public class SheepList<S extends Father> {    private S s;    public S getT() {        return s;    }    public void setT(S s) {        this.s = s;    }    public void vokeFather(S s) {         s.lookRandom();    }}
public class SheepListSub extends SheepList<Son> implements Factory<Son,String>{    @Override    public Son create(String s) {        return new Son( );    }}
public class App {    public static <T> List<T> makeList(T... args) {        List<T> result = new ArrayList<T>();        for (T item : args) {            result.add(item);        }        return result;    }    @SuppressWarnings("unchecked")    public static <T, F> F convert(T t) {        return (F) t;    }    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {        List<String> list = App.makeList("nihao", "hello", "shishui");        Father father = new Son();        Son son = convert(father);        MemiList<? extends Father> mlist0 = new MemiList<Son>() ;        MemiList<Son> mlist = new MemiList<Son>();        mlist.setT(son);        for(TypeVariable<?> ty:mlist.getClass().getTypeParameters()){//<T,Y,U>             System.out.println("MemiList 擦除之后"+ty.getName() );        }        System.out.println("MemiList 擦除之后"+mlist.getClass().getDeclaredField("t").getType().getName());//运行时获得  编译时擦除到object        System.out.println("MemiList 擦除之后 类型"+mlist.getClass().getDeclaredField("t").getGenericType());        System.out.println("MemiList mlist0 擦除之后"+mlist0.getClass().getDeclaredField("t").getType().getName());        SheepList<Son> mlist2 = new SheepList<Son>();        mlist2.vokeFather(son);        System.out.println("MemiList mlist2 擦除之后"+mlist2.getClass().getDeclaredField("s").getType().getName());//运行时获得  编译时擦除到Fahter        Factory f = new SheepListSub();        f.create(new Object());//编写时  没有实际类型填充的静态类型指用  桥接方法  {桥接方法内有checkcast 所以运行时会报错}        //<Son,String>用以编写时{类型推导  :类型正确与否提示;编译时: 生成cast字节码}  运行时的Factory 已经将类型擦除到上界限        Factory<Son,String> f1 = new SheepListSub();        f1.create("asdf");//编写时  有实际类型填充的         SheepListSub f2 = new SheepListSub();        f2.create("asdf");//编写时  静态类型指用    }}

输出:

MemiList 擦除之后TMemiList 擦除之后java.lang.ObjectMemiList 擦除之后 类型TMemiList mlist0 擦除之后java.lang.ObjectlookRandomcom.sanwenyu.init.Son@2524e205try0Exception8finally3MemiList mlist2 擦除之后com.sanwenyu.init.FatherException in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String    at com.sanwenyu.generics.SheepListSub.create(SheepListSub.java:1)    at com.sanwenyu.generics.App.main(App.java:50)

JavaP获得factory<T,F>

public interface com.sanwenyu.generics.Factory<T extends java.lang.Object, F extends java.lang.Object>  SourceFile: "Factory.java"  Signature: #11                          // <T:Ljava/lang/Object;F:Ljava/lang/Object;>Ljava/lang/Object;  minor version: 0  major version: 51  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACTConstant pool:   #1 = Class              #2             //  com/sanwenyu/generics/Factory   #2 = Utf8               com/sanwenyu/generics/Factory   #3 = Class              #4             //  java/lang/Object   #4 = Utf8               java/lang/Object   #5 = Utf8               create   #6 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;   #7 = Utf8               Signature   #8 = Utf8               (TF;)TT;   #9 = Utf8               SourceFile  #10 = Utf8               Factory.java  #11 = Utf8               <T:Ljava/lang/Object;F:Ljava/lang/Object;>Ljava/lang/Object;{  public abstract T create(F);    flags: ACC_PUBLIC, ACC_ABSTRACT    Signature: #8                           // (TF;)TT;}

可以看出 编译期自动为T和F加了上界:

<T extends java.lang.Object, F extends java.lang.Object>

然后将T和F擦除到上界:

 #11 = Utf8               <T:Ljava/lang/Object;F:Ljava/lang/Object;>Ljava/lang/Object;

再来看一个明面上定义了上界的泛型类:SheepList

public class com.sanwenyu.generics.SheepList<S extends com.sanwenyu.init.Father> extends java.lang.Object  SourceFile: "SheepList.java"  Signature: #37                          // <S:Lcom/sanwenyu/init/Father;>Ljava/lang/Object;  minor version: 0  major version: 51  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Class              #2             //  com/sanwenyu/generics/SheepList   #2 = Utf8               com/sanwenyu/generics/SheepList   #3 = Class              #4             //  java/lang/Object   #4 = Utf8               java/lang/Object   #5 = Utf8               s   #6 = Utf8               Lcom/sanwenyu/init/Father;   #7 = Utf8               Signature   #8 = Utf8               TS;   #9 = Utf8               <init>  #10 = Utf8               ()V  #11 = Utf8               Code  #12 = Methodref          #3.#13         //  java/lang/Object."<init>":()V  #13 = NameAndType        #9:#10         //  "<init>":()V  #14 = Utf8               LineNumberTable  #15 = Utf8               LocalVariableTable  #16 = Utf8               this  #17 = Utf8               Lcom/sanwenyu/generics/SheepList;  #18 = Utf8               LocalVariableTypeTable  #19 = Utf8               Lcom/sanwenyu/generics/SheepList<TS;>;  #20 = Utf8               getT  #21 = Utf8               ()Lcom/sanwenyu/init/Father;  #22 = Utf8               ()TS;  #23 = Fieldref           #1.#24         //  com/sanwenyu/generics/SheepList.s:Lcom/sanwenyu/init/Father;  #24 = NameAndType        #5:#6          //  s:Lcom/sanwenyu/init/Father;  #25 = Utf8               setT  #26 = Utf8               (Lcom/sanwenyu/init/Father;)V  #27 = Utf8               (TS;)V  #28 = Utf8               vokeFather  #29 = Methodref          #30.#32        //  com/sanwenyu/init/Father.lookRandom:()I  #30 = Class              #31            //  com/sanwenyu/init/Father  #31 = Utf8               com/sanwenyu/init/Father  #32 = NameAndType        #33:#34        //  lookRandom:()I  #33 = Utf8               lookRandom  #34 = Utf8               ()I  #35 = Utf8               SourceFile  #36 = Utf8               SheepList.java  #37 = Utf8               <S:Lcom/sanwenyu/init/Father;>Ljava/lang/Object;{  public com.sanwenyu.generics.SheepList();    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: invokespecial #12                 // Method java/lang/Object."<init>":()V         4: return      LineNumberTable:        line 5: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       5     0  this   Lcom/sanwenyu/generics/SheepList;      LocalVariableTypeTable:        Start  Length  Slot  Name   Signature            0       5     0  this   Lcom/sanwenyu/generics/SheepList<TS;>;  public S getT();    flags: ACC_PUBLIC    Signature: #22                          // ()TS;    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #23                 // Field s:Lcom/sanwenyu/init/Father;         4: areturn      LineNumberTable:        line 10: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       5     0  this   Lcom/sanwenyu/generics/SheepList;      LocalVariableTypeTable:        Start  Length  Slot  Name   Signature            0       5     0  this   Lcom/sanwenyu/generics/SheepList<TS;>;  public void setT(S);    flags: ACC_PUBLIC    Signature: #27                          // (TS;)V    Code:      stack=2, locals=2, args_size=2         0: aload_0         1: aload_1         2: putfield      #23                 // Field s:Lcom/sanwenyu/init/Father;         5: return      LineNumberTable:        line 14: 0        line 15: 5      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       6     0  this   Lcom/sanwenyu/generics/SheepList;               0       6     1     s   Lcom/sanwenyu/init/Father;      LocalVariableTypeTable:        Start  Length  Slot  Name   Signature            0       6     0  this   Lcom/sanwenyu/generics/SheepList<TS;>;            0       6     1     s   TS;  public void vokeFather(S);    flags: ACC_PUBLIC    Signature: #27                          // (TS;)V    Code:      stack=1, locals=2, args_size=2         0: aload_1         1: invokevirtual #29                 // Method com/sanwenyu/init/Father.lookRandom:()I         4: pop         5: return      LineNumberTable:        line 18: 0        line 19: 5      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       6     0  this   Lcom/sanwenyu/generics/SheepList;               0       6     1     s   Lcom/sanwenyu/init/Father;      LocalVariableTypeTable:        Start  Length  Slot  Name   Signature            0       6     0  this   Lcom/sanwenyu/generics/SheepList<TS;>;            0       6     1     s   TS;}

可以看出类型S被擦出到Father。

#37 = Utf8               <S:Lcom/sanwenyu/init/Father;>Ljava/lang/Object;

s.lookRandom实际是擦除后调用了:Father.lookRandom

1: invokevirtual #29                 // Method com/sanwenyu/init/Father.lookRandom:()I

我们着重看一下继承泛型类或者实现泛型接口时发生的代码变化:
Java泛型只在编译期作用,运行期看不到泛型的意味。编译期的时候 编译期会在代码中自动加入转型和类型检查。

例如我们使用Factory<Son,String>
编写时IDE进行类型实例化限制 f1.后IDE会提示限制后的可调用方法。
如果我们用Factory
f1.后IDE会提示桥接方法。
又比如List<String>
我们在调用get方法时 因为运行时类型被擦除了。编译期要插入 cast代码。

        Factory<Son,String> f1 = new SheepListSub();        f1.create("asdf");//编写时  有实际类型填充的 调用有实例填充后的接口方法 
        Factory f2 = new SheepListSub();        f2.create(new Object());//调用接口桥接方法   会报类型转换错        因为 桥接方法是 先检查是否是静态实际类型 再调用实际覆写方法

我们看下SheepListSub的接口实现方法:

  public com.sanwenyu.init.Son create(java.lang.String);    flags: ACC_PUBLIC    Code:      stack=2, locals=2, args_size=2         0: new           #18                 // class com/sanwenyu/init/Son         3: dup         4: invokespecial #20                 // Method com/sanwenyu/init/Son."<init>":()V         7: areturn      LineNumberTable:        line 9: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0       8     0  this   Lcom/sanwenyu/generics/SheepListSub;               0       8     1     s   Ljava/lang/String;  public java.lang.Object create(java.lang.Object);//桥接方法  编译期自动生成    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC    Code:      stack=2, locals=2, args_size=2         0: aload_0         1: aload_1         2: checkcast     #24                 // class java/lang/String         5: invokevirtual #26                 // Method create:(Ljava/lang/String;)Lcom/sanwenyu/init/Son;         8: areturn      LineNumberTable:        line 1: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature}
之所以要生成桥接方法 是因为                   1 泛型类在编译后擦除到上界 这里是Object                   2 因为虚方法必须是要实现的方法  此时父类或者接口的虚方法是擦除后的类型                   3 子类必须实现这个虚方法【擦除后的类型方法 】 故编译期自动生成以满足这个机制                   4 实际填充类后的方法调用还是 子类中看的见的实现方法

Java 泛型的实际用用 来日方长呀 亲!!

原创粉丝点击