泛型(三)——约束与局限性

  因为java虚拟机没有泛型这一说法,所以在使用java泛型时需要考虑一些限制,大多数限制都是由类型擦除引起的。

1.不能用基本类型实例化类型参数

  不能用类型参数代替基本类型。因此,没有Pair<double>,只有Pair<Double>。当然其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。

2.运行时类型查询只适用于原始类型

  虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。例如:

Pair<String> pair = new Pair<String>();
if(pair instanceof Pair<String>)   //error
if(pair instanceof Pair<T>)  //error
if(pair instanceof Pair)  //true

那么同样的道理,getClass方法总是返回原始类型。例如:

Pair<String> a = new Pair<String>();
Pair<Integer> b = new Pair<Integer>();
System.out.println(a.getClass().equals(b.getClass()));  //输出true

3.不能创建参数化类型数组

  不能实例化参数化类型的数组,例如:

Pair<String> pair = new Pair<String>[10]; //error

这有什么问题呢?擦除之后,pair的类型就变成了Pair[],这明显和我们的本意有区别,我们的本意是想根据不同的类型参数创建不同的表,所以java泛型直接限制不能创建。

4.不能实例化类型变量

  不能使用想new T(...),new T[...]或T.class这样的表达式中的类型变量,例如,下面的Pair<T>构造器就是非法的:

public Pair(){first = new T();}    //error

类型擦除将T改变成Object,而且我们本意肯定不希望调用new Object()。但是,可以通过反射调用Class.newInstance方法来构造泛型对象(在操作数据库时一般用这种方法封装不同的实体)。例如:

public static <T> Pair<T> makePair(Class<T> c1){
      try {
        return new Pair<>(c1.newInstance(),c1.newInstance());
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
}

同理,不能创建一个泛型数组:

public static <T> void minmax(T[] t){
    T[] mm = new T[2]; //error
}   

类型擦除会让这个方法永远构造Object[2]数组,同样我们可以通过反射调用Array.newInstance方法来构造泛型数组。(这样创建在什么地方用我还不知道,以前没用过)

public static <T> void minmax(T[] t){
    T[] mm = (T[]) Array.newInstance(t.getClass().getComponentType(), t.length);
}

5.泛型类的静态上下文中类型变量无效

public class Problem<T>{
    private static T first;  //error
    public static T getFirst(){}  //error
}

6.注意擦除后的冲突

  当泛型类型被擦除时,无法创建引发冲突的条件。下面是一个示例:

public class pair<T>{
    public void equals(T value);
}

这个方法泛型擦除后变成public void equals(Object value),这个方法与Object.equals方法冲突。补救的方法只能重新命名。

7.不能抛出或捕获泛型类的实例

  既不能抛出也不能捕获泛型类对象。实际上,甚至泛型类扩展Throwable都是不合法的,例如以下定义就不能正常编译:

public class Problem<T> extends Exception{}  //error

catch子句不能使用类型变量,例如:以下方法将不能编译

public static <T extends Throwable> void doWork(){
    try {
    } catch (T e) { //error
    }
}

不过在异常规范中使用类型变量是允许的,以下方法是合法的:

public static <T extends Throwable> void doWork(T t) throws T{
    try {
    } catch (Exception e) {
        throw t;
    }
}

参考资料:java核心技术 卷一

时间: 2024-10-19 21:02:49

泛型(三)——约束与局限性的相关文章

Java 泛型的约束与局限性

Java 泛型的约束与局限性 @author ixenos 不能用基本类型实例化类型参数 不能用类型参数代替基本类型:例如,没有Pair<double>,只有Pair<Double>,其原因是类型擦除.擦除之后,Pair类含有Object类型的域,而Object不能存储double值.这体现了Java语言中基本类型的独立状态. 运行时类型查询只适用于原始类型(raw type) 运行时:通常指在Classloader装载之后,JVM执行之时 类型查询:instanceof.getC

Java8基础知识(十)泛型的约束与局限性

泛型的约束与局限性 由于泛型是通过类型擦除.强制类型转换和桥方法来实现的,所以存在某些局限(大多来自于擦除). 不能使用基本类型实例化类型参数 类型参数都是类,要用包装器将基本类型包装才可以作为类型参数(原因在于擦除类型后Object类不能存储基本类型的值).当包装器类不能接受类型参数替换时,可以使用独立的类和方法进行处理. 运行时类型查询只适用于原始类型 由于虚拟机中的对象都有特定的原始类型,所以类型查询只能查询原始类型. // 只能测试a是否为某种Pair类型 if (a instanceo

Java泛型解析(04):约束和局限性

Java泛型解析(04):约束和局限性 前两节,认识和学习了泛型的限定以及通配符,初学者可能需要一些时间去体会到泛型程序设计的好处和力量,特别是想成为库程序员的同学就需要下去体会通配符的运用了,应用程序员则需要掌握怎么使用泛型,这里针对泛型的使用中的约束和局限性做一个介绍性的讲解. 不能用基本类型实例化类型参数 这个很好理解,实例化泛型类时泛型参数不能使用基本类型,如List<int>这是不合法的,要存储基本类型则可以利用基本类型的包装类如List<Integer> .List&l

06.C#泛型约束和高级泛型(三章3.3-3.4)

吃午饭前继上篇泛型再写一篇关于泛型的文章,虽然总是被博客园移出首页,文章不精确实是大问题啊,会再接再厉的.进入正题. 先来说下泛型约束.当我们在使用泛型的时候通常会希望使用泛型实参时,参数能具备某一些特性,这时"泛型约束"来了,它能帮助我们在传入泛型参数,该参数要实现先前指定的约束.有4种约束可用,如下: 引用类型约束:确保使用的类型参数是引用类型(T:class,且必须是类型参数指定的第一个约束),类型实参任何类.接口.数组.委托.或者已知是引用类型的另一个类型参数. 值类型约束:表

计算机程序的思维逻辑 (37) - 泛型 (下) - 细节和局限性

35节介绍了泛型的基本概念和原理,上节介绍了泛型中的通配符,本节来介绍泛型中的一些细节和局限性. 这些局限性主要与Java的实现机制有关,Java中,泛型是通过类型擦除来实现的,类型参数在编译时会被替换为Object,运行时Java虚拟机不知道泛型这回事,这带来了很多局限性,其中有的部分是比较容易理解的,有的则是非常违反直觉的. 一项技术,往往只有理解了其局限性,我们才算是真正理解了它,才能更好的应用它. 下面,我们将从以下几个方面来介绍这些细节和局限性: 使用泛型类.方法和接口 定义泛型类.方

Mysql(三)约束

一.视图 视图是虚拟的数据表,本身不存储数据,而是提供数据的逻辑 展示.       1.创建视图 create view stu_view as select s1.id, s1.name, s2.room, s2.stay_time from student s1, stay s2 where s1.id = s2.id; 创建视图后,就可以像查询数据表一样查询视图.  视图的字段就是我们从数据表中查询而来的字段. select id from stu_view   desc stu_vie

Java 泛型 三

一.泛型初衷 Java集合不会知道我们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要就具有很好的通用性.但这样做也带来两个问题: –集合对元素类型没有任何限制,这样可能引发一些问题:例如想创建一个只能保存Sting对象的集合,但程序也可以轻易地将int对象"丢"进去,所以可能引发异常. –由于把对象"丢进"集合时,集合丢失了对象的状态信息,集合只知道它盛装的是Object,因此取出集合元素后通常还需要进行强制类型转换.这种强制类型转换既

MySQL(三) —— 约束以及修改数据表

约束: 1. 约束保证数据的完整性和一致性: 2. 约束分为表级约束和列级约束: 3. 约束类型包括:NOT NULL, PRIMARY KEY, UNIQUE KEY, DEFAULT, FOREIGN KEY 外键约束 父表和子表必须使用相同的存储引擎,而且禁止使用临时表: 数据库的存储引擎只能为InnoDB: 外键列和参照列必须具有相似的数据类型.其中数字的长度或是否有符号位必须相同:而字符的长度则可以不同: 外键列和参照列必须创建索引.如果外键列不存在索引的话,MySQL将自动创建索引.

java通过反射获取私有的构造方法,及反射擦除泛型数据类型约束

/* * 反射获取私有的构造方法运行 * 不推荐,破坏了程序的安全性,封装性 * 暴力私有 */ public class ReflectDemo4 { public static void main(String[] args)throws Exception { Class c=Class.forName("demo01.Person"); Constructor con=c.getDeclaredConstructor(int.class,String.class); //Con