泛型基础:
泛型的常见问题
/*泛型是在不确定类型的情况下使用。用与在使用时再确定属性方法实例要操作的类型
* 1.静态方法泛型必须定义在方法上。不能使用类的泛型。
* 2.方法的泛型可以和类的泛型不同。
3.当操作对象与泛型不一致时发生编译错误。泛型就是提供给编译器的安全检查机制,可以作为安全检查机制来使用。
4.当类,接口,方法操作的对象不确定类型时。使用泛型。泛型在继承和实现时可以连续传递。
class asd<E>{
public static <Q>void sdf(Q q){
}
public <E> void dfg(E e){
}
}
//此接口要操作的对象不确定。
interface inter<E>{
public void show(E e);
}
//此类要操作的对象明确为String.
class jk implements inter<String>{
public void show(String e) {
}
}
// 此类所操作的对象不确定类型。
class ko<E> implements inter<E>{
public void show(E e) {
}
}
class student{
}
class worker{
}
class asd<E>{
void sdf(E e){
}
<Q> void fgh(Q q)
}
<E>void ghj(E e){
}
static void jlk(E e){
//错误 不能直接使用类的泛型。
}
static<E> void jlsk(E e){
//正确 必须自定义泛型
}
}
* 可以对类型进行限定:
* ? extends E:接收E类型或者E的子类型对象。上限! *
* ? super E :接收E类型或者E的父类型。下限!
一般使用上限 通常对集合中的元素进行取出操作时,可以是用下限。
泛型擦除: 当编译后,泛型就不存在了。
泛型代码和虚拟机
- 问题: 编译器如何擦除类型变量,变为确定的类型?,对虚拟机来说,所有对象都属于普通类。
- 每定义一个泛型就会对应有一个原始类型,。擦除(erased) 类型变M, 并替换为限定类型(无 限定的变量用 Object)。
- 其原始类型以第一限定 Comparable 为准。若某接口仅作为标记,应放再右边,因为编译器可能对 Comparable 进行强转
-
翻译泛型表达式
- 编译器再擦除泛型是,将会自动插入强制类型转换,最后生成字节码文件。编译器把这个方法调用翻译为两条虚拟机指令:
- 当存取泛型域是也要插入强转
-
java泛型转换的事实
- •虚拟机中没有泛型,只有普通的类和方法。
- •所有的类型参数都用它们的限定类型替换。
- •桥方法被合成来保持多态。
- •为保持类型安全性,必要时插人强制类型转换
-
泛型约束和局限性
- 不能用类型参数代替基本类型。因此, 没有 Pair<double>, 只 有 Pair<Double>
- 运行时类型查询只适用于原始类型,instanceof 对Pair<String> 治测试是否是pair. getClass方法也治返回pair.class
- 不能创建参数化类型的数组,即泛型数组不可用,可以使用List集合替代。
- Varargs警告:java不支持泛型数组,但在接受多个泛型参数时,虚拟机会自动创建一个泛型数组。但会得到一个警告。
- public static <T> void addAll(Collections coll, T... ts) ts会被创建为泛型数组。
- 该警告: @SafeVarargs 或者 @SuppressWamings("unchecked")标注。
- 在泛型类中不能对T实例化,因为类型擦除 T -》 Object ,将会创建Object实例,无意义。
- T.class 不合法,若要创建T的实例,必须有实际T的class传入,该方法可以依靠这个class创建实例。
- 禁止使用带有类型变量 的静态域和方法。即不能使用泛型类的泛型。
- 至泛型类扩展 Throwable 是不合法的。public class Problem<T> extends Exception { /* ...*/ } // Error can't extend Throwable
- catch 子句中不能使用类型变量。catch (T e) // Error can't catch type variable
- 在异常规范中使用类型变量是允许的。public static <T extends Throwable〉void doWork(T t) throws T // OK
- 消除对已检查异常的检查(欺骗编译器使其认为这是一个 RuntimeException)
-
public void asd() throws Throwable { Throwable t = new Throwable(); System.out.println(t+"1"); try{ throw t; }catch (Throwable w){ //使用泛型欺骗编译器,使其认为抛出了未检查异常,此时不需要 throws Throwable mytest.<RuntimeException>throwAs(w); System.out.println(w+"2"); //此时需要 throws Throwable,否则编译不通过。 adsd(); } } //抛出已检查异常 public void adsd() throws Throwable { throw new Throwable(); } //使用泛型欺骗编译器,使其认为抛出了未检查异常 @SuppressWarnings("unchecked") public static <T extends Throwable> void throwAs(Throwable e)throws T{ T t = (T) e; System.out.println(t+"3"); throw t; }
- 这里说明,该泛型方法确实会欺骗编译器。但是由输出中间结果看到 为Throwable3 ,而不是Runtime3。即其实际抛出的仍为 Throwable ,但为什么编译器会认为这是一个未检查异常。???????????