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 泛型的实际用用 来日方长呀 亲!!
- Java 泛型机制
- Java泛型机制
- Java泛型机制
- Java中的泛型机制
- Java 泛型机制解析
- 【javaSE】Java泛型机制
- java泛型机制剖析
- JAVA泛型擦除机制
- Java泛型机制笔记
- java 泛型机制学习
- [黑马程序员]--Java语言泛型机制
- JAVA泛型的潜在类型机制
- Java集合框架和泛型机制
- Java泛型--解析泛型擦除机制
- C++泛型 && Java泛型实现机制
- java---泛型和反射机制
- Java集合框架和泛型机制
- Java泛型类型擦除机制
- 快速生成Plugman中的"lib-file"、"source-file"的xml内容
- 较复杂的sql语句
- 反射
- HDU
- Sequelize API简记
- Java泛型机制
- 【Python学习】 之 Turtle库
- MVP+Retrofit+RxJava实现分类
- 学习笔记--Java开发环境
- LeetCode-014 Longest Common Prefix
- 斯坦福大学深度学习公开课cs231n学习笔记(7)神经网络防止数据过拟合:损失函数和正则化
- (三)Requests库
- 剑指offer—两个链表的第一个公共节点
- 【游戏开发3D数学笔记】6.坐标系切换和纹理