标签:article 编译器 syn 兼容 its length 对比 整理 pac
public static void main(String[] args) { List<String> stringList = new ArrayList<String>(); List<Integer> integerList = new ArrayList<Integer>(); Class classStringArrayList = stringList.getClass(); Class classIntegerArrayList = integerList.getClass(); System.out.println(classStringArrayList.getClass() == classIntegerArrayList.getClass()); } // print true
public class Pair<T> { private T field; }
Pair<String> p = new Pair<String>();
,field 则被指定为 String 类型。public interface Generator<T> { public T next(); }
/** * 即:class FruitGenerator<T> implements Generator<T> { * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */ class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
/** * 传入泛型实参时: * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口 Generator<T> * 但是我们可以为 T 传入无数个实参,形成无数种类型的 Generator 接口。 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 * 即:Generator<T>,public T next(); 中的的 T 都要替换成传入的 String 类型。 */ public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } }
public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; }
public class StaticGenerator<T> { .... /** * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 * 如:public static void show(T t){..},此时编译器会提示错误信息: "StaticGenerator cannot be refrenced from static context" */ public static <T> void show(T t) { } }
泛型方法和可变参数
public <T> void printMsg( T... args){ for(T t : args){ System.out.println(t); } }
// JDK 1.7 之前 Map<String, String> map = new HashMap<String, String>(); // JDK 1.7 类型推断 Map<String, String> map = new HashMap<>();
上限(extends)子类型通配符
下限(super)超类型通配符
List<String>[] ls = new ArrayList<String>[10];
List<?>[] ls = new ArrayList<?>[10];
List<String>[] ls = new ArrayList[10];
List<String>[] lsa = new List<String>[10]; // Not really allowed. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Unsound, but passes run time store check String s = lsa[1].get(0); // Run-time error: ClassCastException.
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Correct. Integer i = (Integer) lsa[1].get(0); // OK
泛型的类型参数只能是类类型,不能是简单类型
类型检查不可使用泛型
public class Test { Class<?> aClass; public Test(Class<?> aClass) { this.aClass = aClass; } public boolean isInstance(Object object) { return aClass.isInstance(object); } public static void main(String[] args) { Test test = new Test(A.class); System.out.println(test.isInstance(new A())); System.out.println(test.isInstance(new B())); } public static class A { } public static class B { } } /** print true false **/
不能实例化泛型对象
T t= new T();//error T.class.newInstance();//error T.class;//error
Class<T> t
参数,调用 t.newInstance()
。public void sayHi(Class<T> c){ T t=null; try { t=c.newInstance(); } catch (Exception e) { e.printStackTrace(); } System.out.println("Hi "+t); }
不能在泛型类的静态域中使用泛型类型
private T t
,这是对象级别的,对于泛型类型变量来说在对象初始化时才知道其具体类型。
public class Singleton<T>{ private static T singleton; //error public static T getInstance(){} //error public static void print(T t){} //error }
public static <T> T getInstance(){return null;} //ok public static <T> void print(T t){} //ok
不能捕获泛型类型的对象
public class GenericThrowable<T> extends Throwable{ //The generic class GenericThrowable<T> may not subclass java.lang.Throwable }
@Test public void testGenericThrowable(){ GenericThrowable<RuntimeException> obj=new GenericThrowable<RuntimeException>(); obj.doWork(new RuntimeException("What did you do?")); } public static class GenericThrowable<T extends Throwable>{ public void doWork(T t) throws T{ try{ Reader reader=new FileReader("notfound.txt"); //这里应该是checked异常 }catch(Throwable cause){ t.initCause(cause); throw t; } } }
Student<String> student = new Student<String>(); student.setScore("优秀"); System.out.println(student.getScore()); //泛型嵌套 School<Student<String>> school = new School<Student<String>>(); school.setStu(student); String s = school.getStu().getScore(); //从外向里取 System.out.println(s); // hashmap 使用了泛型的嵌套 Map<String, String> map = new HashMap<String,String>(); map.put("a", "张三"); map.put("b", "李四"); Set<Entry<String, String>> set = map.entrySet(); for (Entry<String, String> entry : set) { System.out.println(entry.getKey() + ":" + entry.getValue()); }
class Parent<T extends SQLException>{ public void test() throws T{} } class Son extends Parent<BatchUpdateException>{ @Override public void test() throws BatchUpdateException{} //这里必须与参数类型保持一致,否则编译不通过。 }
class Super<SQLException>{ public void test() throws SQLException{} }
与 Son 类对比,发现并没有违背 Java 中方法重写(Override)的规则。
情况 4 中 T 作为返回类型时被擦除,因为协变返回类型的存在,所以不会有问题。
class Parent<T>{ T test(){} } class Son extends Parent<String>{ @Override protected String test(){} //protected 拥有比 package 更高的访问权限,可以被同一包内的类访问 }
class Super{ Object test(){} }
public class Test { private Object obj; public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public static void main(String[] args) { Test test = new Test(); test.setObj("test"); String testString = (String) test.getObj(); } }
public class Test { public Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; public java.lang.Object getObj(); descriptor: ()Ljava/lang/Object; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field obj:Ljava/lang/Object; 4: areturn LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; public void setObj(java.lang.Object); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #2 // Field obj:Ljava/lang/Object; 5: return LineNumberTable: line 10: 0 line 11: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this LTest; 0 6 1 obj Ljava/lang/Object; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #3 // class Test 3: dup 4: invokespecial #4 // Method "<init>":()V 7: astore_1 8: aload_1 9: ldc #5 // String test 11: invokevirtual #6 // Method setObj:(Ljava/lang/Object;)V 14: aload_1 15: invokevirtual #7 // Method getObj:()Ljava/lang/Object; 18: checkcast #8 // class java/lang/String 21: astore_2 22: return }
public class Test<T> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } public static void main(String[] args) { Test<String> test = new Test<String>(); test.setObj("test"); String string = test.getObj(); } }
public class Test<T extends java.lang.Object> extends java.lang.Object { public Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; LocalVariableTypeTable: Start Length Slot Name Signature 0 5 0 this LTest<TT;>; public T getObj(); descriptor: ()Ljava/lang/Object; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field obj:Ljava/lang/Object; 运行期为 Object 类型 4: areturn LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; LocalVariableTypeTable: Start Length Slot Name Signature 0 5 0 this LTest<TT;>; Signature: #25 // ()TT; public void setObj(T); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #2 // Field obj:Ljava/lang/Object; 5: return LineNumberTable: line 10: 0 line 11: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this LTest; 0 6 1 obj Ljava/lang/Object; LocalVariableTypeTable: Start Length Slot Name Signature 0 6 0 this LTest<TT;>; 0 6 1 obj TT; Signature: #28 // (TT;)V public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #3 // class Test 3: dup 4: invokespecial #4 // Method "<init>":()V 7: astore_1 8: aload_1 9: ldc #5 // String test 11: invokevirtual #6 // Method setObj:(Ljava/lang/Object;)V 14: aload_1 15: invokevirtual #7 // Method getObj:()Ljava/lang/Object; 18: checkcast #8 // class java/lang/String 类型转换为编译器自动添加 21: astore_2 22: return } Signature: #37 // <T:Ljava/lang/Object;>Ljava/lang/Object;
set()
方法,在编译器可以做类型检查,非法类型不能通过编译。get()
方法,由于擦除机制,运行时的实际引用类型为 Object 类型。main()
方法主体第 18 行有类型转换的逻辑,这是编译器自动添加的。Signature: #37 // <T:Ljava/lang/Object;>Ljava/lang/Object;
重载与重写
setName()
方法。public class Parent { private Object obj; public void setName(Object name) { System.out.println("Parent:" + name); } public Object getName() { return obj; } } public class Son extends Parent { private String obj; public void setName(String name) { System.out.println("Son:" + name); } public String getName() { return obj; } public static void main(String[] args) { Son son = new Son(); son.setName("abc"); son.setName(new Object()); } } /** print Son:abc Parent:java.lang.Object@1b6d3586 ** /
public class Parent<T> { public void setName(T name) { System.out.println("Parent:" + name); } }
public class Parent { public void setName(Object name) { System.out.println("Parent:" + name); } }
son.setName(new Object())
提示了错误,造成了重载无效。public class Son extends Parent<String> { public void setName(String name) { System.out.println("Son:" + name); } public static void main(String[] args) { Son son = new Son(); son.setName("abc"); son.setName(new Object());//The method setName(String) in the type Son is not applicable for the arguments (Object) } }
setName(String)
是否标注为 @Override
都将是重写而不是重载。setName()
方法。
setName(Object)
方法,解决了多态问题。setName(java.lang.Object)
有关键字 ACC_BRIDGE 指定为桥方法,关键字 ACC_SYNTHETIC 表示这个方法是由编译器自动生成。public void setName(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: ldc #5 // String Son: 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: return public void setName(java.lang.Object); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: checkcast #15 // class java/lang/String 5: invokevirtual #13 // Method setName:(Ljava/lang/String;)V 8: return
getName()
方法,只是返回类型不同。public java.lang.String getName(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #9 // Field obj:Ljava/lang/String; 4: areturn LineNumberTable: line 9: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LSon; public java.lang.Object getName(); descriptor: ()Ljava/lang/Object; flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokevirtual #14 // Method getName:()Ljava/lang/String; 4: areturn LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LSon;
Java 虚拟机使用参数类型和返回类型确定一个方法。
getName()
,所以禁止出现这种情况),Java 虚拟机是能够分清楚这些方法的,前提是需要返回类型不一样(在运行期,Java 虚拟机有足够的方法去区分这种二义性,比如用 ACC_BRIDGE 或 ACC_SYNTHETIC,所有就允许了这种情况出现)。为 Parent 增加方法 equals()
。
public boolean equals(T value){ return (obj.equals(value)); }
equals(T)
方法时,第一反应是 equals(T)
没有覆盖住父类 Object 中的equals(Object)
方法。equals()
方法一致,造成了冲突(两个方法都有相同的擦除,但都不重写另一个)。继承泛型的参数化
import java.util.Comparator; public class Parent implements Comparator { @Override public int compare(Object o1, Object o2) { return 0; } } import java.util.Comparator; public class Son extends Parent implements Comparator { }
import java.util.Comparator; public class Parent implements Comparator<Parent> { @Override public int compare(Parent o1, Parent o2) { return 0; } } import java.util.Comparator; public class Son extends Parent implements Comparator<Son> { }
List<? extends Parent> list = new ArrayList<Son>(); //协变 List<? super Son> list2 = new ArrayList<Parent>(); //逆变
List<Parent> list = new ArrayList<Son>(); List<Object> list2 = new ArrayList<Parent>();
里氏替换原则
协变与逆变的定义
class Parent { Number method(Number n) { ... } } class Son extends Parent { @Override Number method(Number n) { ... } }
class Parent { Number method(Number n) { ... } } class Son extends Parent { @Override Integer method(Number n) { ... } }
PECS 原则
public class Son extends Parent<String> {}
public class Son<T> extends Parent<T> {}
Pair<Son> s = new Pair<>(); Pair<Parent> p = s; //error
public class Parent<T> { private T name; public T getName() { return name; } public void setName(T name) { this.name = name; } public static void main(String[] args) { Parent<String> p1 = new Parent<>(); p1.setName("abc"); System.out.println(p1.getName()); Parent p2 = p1; p2.setName(new File("1.txt")); //error System.out.println(p2.getName()); } } /** print abc 1.txt **/
Class<T>.getGenericSuperclass()
获取泛型超类。ParameterizedType
类型参数实体类。
http://www.sohu.com/a/245549100_796914
https://www.imooc.com/article/18159
https://blog.csdn.net/tyrroo/article/details/80930938
https://blog.csdn.net/wang__qin/article/details/81415223
https://segmentfault.com/a/1190000014824002
https://www.runoob.com/java/java-generics.html
https://www.cnblogs.com/coprince/p/8603492.html
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html
标签:article 编译器 syn 兼容 its length 对比 整理 pac
原文地址:https://www.cnblogs.com/youngao/p/12576440.html