本文共 8459 字,大约阅读时间需要 28 分钟。
ArrayList<String> files = new ArrayList<String>();
ArrayList<String> files = new ArrayList<>();
public class Pair<T> {...}
public class Pair<T, U> {...}
public static <T> T getMiddle(T... a)
class ArrayAlg { public staticgetMiddle(T... a) { return a[a.length / 2]; }}
10.当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型:
String middle = ArrayAlg.<String> getMiddle(“John”,“Q.”,“Public”);
11. 可以通过多类型变量设置限定来保证调用安全: public static <T extends Comparable> T min(T[] a)
注意这里使用关键字extends,而非implements 12. 一个类型变量或通配符可以有多个限定,限定类型之间用&分隔: T extends Comparable & Serializable
在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有1个类。如果用一个类作为限定,它必须是限定列表中的第一个 public class Pair{ private T first; private T second; public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; } public void setSecond(T newValue) { second = newValue; }}class DateInterval extends Pair { public void setSecond(Date second) { if(second.compareto(getFirst)) { super.setSecond(second); } }}类型擦除后:class DateInterval extends Pair { // 桥方法 public void setSecond(Object second) { setSecond((Date) second); } public void setSecond(Date second) { if(second.compareto(getFirst)) { super.setSecond(second); } }}
6.在虚拟机中,用方法名、参数类型和返回类型来确定一个方法
7. 有关Java泛型转换的事实: ·虚拟机中没有泛型,只有普通的类和方法。 ·所有的类型参数都用它们的限定类型替换。 ·桥方法被合成来保持多态。 ·为保持类型安全性,必要时插入强制类型转换if(a instanceof Pair<String>) //Error
实际上仅仅测试a是否为任意类型的Pair。 类似地,强制类型转换: Pair<String> p = (Pair<String>)a;
//WARNING!--can only test that a is a pair.
同样的道理,getClass()将总是返回原始的类型: Pair<String> stringPair = ... Pair<Employee> employeePair = ... if(stringPair.getClass() == employeePair.getClass()) //they are equal.
其结果将返回true,因为两次调用getClass()返回的都是Pair.classPair<String>[] table = new Pair<String>[10]; //ERROR
以上代码无法通过编译,因为泛型的擦除机制会绕过数组的类型检查,导致类型错误。 可以声明类型为Pair[]的变量,但不能用new Pair[10]来初始化它public staticvoid addAll(Collection coll, T... ts) { for(t:ts) coll.add(t);}
调用该方法的过程中将有可能创建一个以参数化类型的数组,违法规则3,但对于这种情况,编译器有所放松,仅仅会得到一个警告
5. 不能实例化类型变量。new T(…)、new T[…]或T.class的表达式均为非法。但是可以通过反射调用Class.newInstance()方法来构建新对象:public staticPair makePair(Class cl) { try { return new Pair<>(cl.newInstance(), cl.newInstance()); } catch (Exception ex) { return null; }}
上面的方法可以按照下列方式调用:
Pair<String> p = Pair.makePair(String.class);
Class本身是泛型,String.class 是一个Class的实例 6. 泛型类的静态上下文中类型变量无效。不鞥呢在静态域或方法中引用类型变量,例如: public class Singleton{ private static T singleInstance; //ERROR public static T getSingleInstance() { //ERROR if(singleInstance == null)construct new instance of T return singleInstance; } }}
如果这个程序能够运行,就可以声明一个Singleton,同时声明一个Singleton,但是这个程序无法工作,类型擦除以后,只剩下Singleton类,它只包含一个singleInstance域
7. 不能抛出或捕获泛型类实例:public class Problemextends Exception { /* ... */}//Error--can’t extend Throwable.不过,在异常规范中使用类型变量是允许的。以下方法是合法的:public static void doWork(T t) throws T { //OK try { do work } catch (Throwable realCause) { t.initCause(realCause); throw t; }}
8.注意擦除后的冲突。当泛型类被擦除时,无法创建引发冲突的条件,下面的代码无法通过编译:
public boolean equals(T t) {...};
类型擦除后,它将于Object类中的equals方法发生冲突。 泛型类的另一个原则是:要想支持擦除的转换,就需要限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化。例如,下述代码是非法的: class Calendar implements Comparable{ ...}class GrogorianCalendar extends Calendar implements Comparable { ...} // ERROR
PairmanagerBuddies = new Pair<>(ceo, cfo);Pair rawBuddies = managerBuddies; //OKrawBuddies.setFirst(new File(“...”));// only a compile-time warning.
3.泛型类可以拓展或实现其他的泛型类,如下图:
4. 使用通配符可以表示一组泛型类型,例如:Pair<? extends Employee>
表示任何类型参数是Employee子类的泛型类,包括Pair<Manager>
,但不包括Pair<String>
5. 子类型限定通配符泛型类无法向其中的方法传递参数,例如调用Pair<? extends Employee>
类中的setFirst方法将引发编译错误: Pair<Manager> managerBuddies = new Pair<>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; //OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error.
以上代码编译不通过,因为 ?不可以匹配任何类型 6. 可以以超类型的方式指定通配符限定,如下:? super Manager
这个通配符将限制为Manager类的所有超类型 7. 超类型限定通配符泛型无法提供返回值,例如,不可以调用Pair<? super Manager>
的getFirst方法:? super Manager getFirst();
因为编译器不知道返回值的确切类型,只能把它赋给一个Object(不会编译出错) 8. 直观地讲,带有超类型限定的通配符可以向泛型类写入(但是,可写入null),带有子类型对象的通配符可从泛型类中读取 9. 还可以使用无限定通配符,例如:Pair<?>
,它和原始的Pair类不同。Pair<?>
类的getter方法只能赋给Object,Setter方法则不能调用,甚至不能用Object调用 10. 通配符不是类型变量,因此,不能在编写代码中使用“?”作为一种类型,下述代码是非法的: public static void swap(Pair p) { ? t = p.getFirst() // ERROR p.setFirst(p.getSecond); p.setSecond(t);}
11.可以通过一个辅助方法来解决上述通配符捕获的问题:
public staticvoid swapHelper(Pair p) { T t = p.getFirst(); p.setFirst(p.getSecond()); p.setSecond(t);}
然后,调用该方法来解决上述问题:
public static void swap(Pair p) { swapHelper(p);}
public static void maximinBonus(Manager[] a, Pair result) { minmax(a, result); PairAlg.swap(result);// OK--swapHelper captures wildcard type}
13.通配符捕获只有在许多限制的情况下才是合法的。编译器必须能够确信通配符表达的是单个、确定的类型。例如,ArrayList<Pair<T>>
中的T永远不能捕获ArrayList<Pair<?>>
中的通配符。数组列表中可以保存两个Pair<?>
,分别针对?的不同类型
Class<String>
类的对象,并且是唯一的对象T newInstance();T cast(Object obj)T[] getEnumConstants();Class getSuperClass()ConstructorgetConstructor(Class... parameterTypes);Constructor getDeclaredConstructor(Class.. parameterTypes);
3.常用方法:
java.lang.Class1.0·T newInstance() //返回默认构造器构造的一个新实例。·T cast(Object obj) 5.0 //如果obj为null或有可能转换成类型T,则返回obj,否则抛出BadCastException异常。·T[] getEnumConstants() 5.0 //如果T是枚举类型,则返回所有值组成的数组,否则返回null。·Class getSuperclass() 5.0 //返回这个类的超类,如果T不是一个类或Object类,则返回null。·Constructor getConstructor(Class... parametertypes) 5.0·Constructor getDeclaredConstructor(Class... parametertypes) 5.0 //获得共有的构造器或带有指定参数类型的构造器。java.lang.reflect.Constructor 1.1·T newInstance(Object ... paramters) 5.0 //返回用指定参数构造的新实例
4.为了表达泛型类型声明,JavaSE 5.0在java.lang.reflect包中提供了一个新的接口Type。这个接口包含下列子类型:
·Class类,描述具体类型。·TypeVariable接口,描述类型变量(如T extends Comparable )。·WildcardType接口,描述通配符(如? super T)。·ParameterizedType接口,描述泛型类或接口类型(如Comparable )。·GenericArrayType接口,描述泛型数组(如T[])
5. 常用方法:
java.lang.Class1.0·TypeVariable[] getTypeParameters() 5.0//如果这个类型被声明为泛型类型,则获得泛型类型变量,否则获得一个长度为0的数组。·Type getGenericSuperclass() 5.0//获得被声明为这一类型的超类的泛型类型;如果这个类型是Object或不是一个类类型(class type),则返回null。·Type[] getGenericInterfaces() 5.0//获得被声明为这个类型的接口的泛型类型(以声明次序),否则,如果这个类没有实现接口,返回长度为0的数组。java.lang.reflect.Method 1.1·TypeVariable[] getTypeParameters() 5.0//如果这个方法被声明为泛型方法,则获得泛型类型变量,否则返回长度为0的数组。·Type getGenericReturnType() 5.0//获得这个方法被声明为泛型的返回类型。·Type[] getGenericParameterTypes() 5.0//获得这个方法被声明的泛型参数类型。如果这个方法没有参数,返回长度为0的数组。java.lang.reflect.TypeVariable 5.0·String getName()//返回类型变量的名字。·Type[] getBounds()//获得这个类型变量的子类限定,否则,如果该变量无限定,则返回长度为0的数组。java.lang.reflect.WildcardType 5.0·Type[] getUpperBounds()//获得这个类型变量的子类(extends)限定,否则,如果没有子类限定,则返回长度为0的数组。·Type[] getLowerBounds()//获得这个类型变量的超类(super)限定,否则,如果没有超类限定,则返回长度为0的数组。java.lang.reflect.ParameterizedType 5.0·Type getRowType()//获得这个参数化类型的原始类型。·Type[] getActualTypeArguments()//获得这个参数化类型声明时所使用的类型参数。·Type getOwnerType()//如果是内部类型,则返回其外部类型,如果是一个顶级类型,则返回null。java.lang.reflect.GenericArrayType 5.0·Type getGenericComponentType()//获得声明该数组类型的泛型组合类型