Java泛型机制

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 擦除之后T
MemiList 擦除之后java.lang.Object
MemiList 擦除之后 类型T
MemiList mlist0 擦除之后java.lang.Object
lookRandomcom.sanwenyu.init.Son@2524e205
try0
Exception8
finally3
MemiList mlist2 擦除之后com.sanwenyu.init.Father
Exception 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.Objec
t>
  SourceFile: "Factory.java"
  Signature: #11                          // <T:Ljava/lang/Object;F:Ljava/lang/Object;>Ljava/lang/Ob
ject;
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant 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.Objec
t>

然后将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.O
bject
  SourceFile: "SheepList.java"
  Signature: #37                          // <S:Lcom/sanwenyu/init/Father;>Ljava/lang/Object;
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant 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/F
ather;
  #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/ini
t/Son;
         8: areturn
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}
之所以要生成桥接方法 是因为

                   1 泛型类在编译后擦除到上界 这里是Object
                   2 因为虚方法必须是要实现的方法  此时父类或者接口的虚方法是擦除后的类型
                   3 子类必须实现这个虚方法【擦除后的类型方法 】 故编译期自动生成以满足这个机制
                   4 实际填充类后的方法调用还是 子类中看的见的实现方法

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值