转自:
http://www.myexception.cn/mobile/692129.html
向作者致谢!
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
大多数这些需要注意的问题,都是由类型擦除引起的。
一、基本类型
不能用类型参数替换基本类型。就比如,没有ArrayList<double>,只有ArrayList<Double>。因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储double值,只能引用Double的值。
二、运行时类型查新
举个例子:
ArrayList<String> arrayList=new ArrayList<String>();
因为类型擦除之后,ArrayList<String>只剩下原始类型,泛型信息String不存在了。
那么,运行时进行类型查询的时候使用下面的方法是错误的
if( arrayList instanceof ArrayList<String>)
java限定了这种类型查询的方式
if( arrayList instanceof ArrayList<?>)
三、异常
1、不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法。例如:下面的定义将不会通过编译:
public class Problem<T> extends Exception{......}
为什么不能扩展Throwable,因为异常都是在运行时捕获和抛出的,而在编译的时候,泛型信息全都会被擦除掉,那么,假设上面的编译可行,那么,在看下面的定义:
try{
}catch(Problem<Integer> e1){
。。
}catch(Problem<Number> e2){
...
}
类型信息被擦除后,那么两个地方的catch都变为原始类型Object,那么也就是说,这两个地方的catch变的一模一样,就相当于下面的这样
try{
}catch(Problem<Object> e1){
。。
}catch(Problem<Object> e2){
...
这个当然就是不行的。就好比,catch两个一模一样的普通异常,不能通过编译一样:
try{
}catch(Exception e1){
。。
}catch(Exception e2){//编译错误
...
2、不能再catch子句中使用泛型变量
public static <T extends Throwable> void doWork(Class<T> t){
try{
...
}catch(T e){ //编译错误
...
}
}
因为泛型信息在编译的时候已经变味原始类型,也就是说上面的T会变为原始类型Throwable,那么如果可以再catch子句中使用泛型变量,那么,下面的定义呢:
public static <T extends Throwable> void doWork(Class<T> t){
try{
...
}catch(T e){ //编译错误
...
}catch(IndexOutOfBounds e){
}
}
,根据异常捕获的原则,一定是子类在前面,父类在后面,那么上面就违背了这个原则。即使你在使用该静态方法的使用T是ArrayIndexOutofBounds,在编译之后还是会变成Throwable,ArrayIndexOutofBounds是IndexOutofBounds的子类,违背了异常捕获的原则。所以java为了避免这样的情况,禁止在catch子句中使用泛型变量。
但是在异常声明中可以使用类型变量。下面方法是合法的。
public static<T extends Throwable> void doWork(T t) throws T{
try{
...
}catch(Throwable realCause){
t.initCause(realCause);
throw t;
}
}
上面的这样使用是没问题的。
四、数组
不能声明参数化类型的数组。如:
Pair<String>[] table = newPair<String>(10); //ERROR
这是因为擦除后,table的类型变为Pair[],可以转化成一个Object[]。
Object[] objarray =table;
数组可以记住自己的元素类型,下面的赋值会抛出一个ArrayStoreException异常。
objarray ="Hello"; //ERROR
对于泛型而言,擦除降低了这个机制的效率。下面的赋值可以通过数组存储的检测,但仍然会导致类型错误。
objarray =new Pair<Employee>();
提示:如果需要收集参数化类型对象,直接使用ArrayList:ArrayList<Pair<String>>最安全且有效。
五、泛型类型的实例化
不能使用像new T(...),new T[...],T.class这样的表达式
不能实例化泛型类型。如,
first = newT(); //ERROR
是错误的,类型擦除会使这个操作做成new Object()。
可以使用Class.newInstance()方法:
cl.newInstanc();
不能建立一个泛型数组。
public<T> T[] minMax(T[] a){
T[] mm = new T[2]; //ERROR
...
}
类似的,擦除会使这个方法总是构靠一个Object[2]数组。但是,可以用反射构造泛型对象和数组。
利用反射,调用Array.newInstance:
publicstatic <T extends Comparable> T[]minmax(T[] a)
{
T[] mm == (T[])Array.newInstance(a.getClass().getComponentType(),2);
...
// 以替换掉以下代码
// Obeject[] mm = new Object[2];
// return (T[]) mm;
}
六、静态上下文
不能在静态域或者方法中引用类型变量。
public class Test<M> {
public static void test(M m) { //error
}
}
这句话的错误提示是无法在静态上下文中引用非静态类 M.
因为test方法是静态的可以用类名直接调用,而M只有在类Test创建对象的时候才会明确.
test静态方法存在的时候还没明确M是什么.
所以静态方法不可以访问类上定义的泛型.
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上.
可以改成:
public class Test {
public static <M> void test(M m) { //right
}
}
七、类型擦除后的冲突
1、
当泛型类型被擦除后,创建条件不能产生冲突。如果在Pair类中添加下面的equals方法:
class Pair<T> {
public boolean equals(T value) {
return null;
}
}
考虑一个Pair<String>。从概念上,它有两个equals方法:
booleanequals(String); //在Pair<T>中定义
boolean equals(Object); //从object中继承
但是,这只是一种错觉。实际上,擦除后方法
booleanequals(T)
变成了方法 booleanequals(Object)
这与Object.equals方法是冲突的!当然,补救的办法是重新命名引发错误的方法。
2、
泛型规范说明提及另一个原则“要支持擦除的转换,需要强行制一个类或者类型变量不能同时成为两个接口的子类,而这两个子类是同一接品的不同参数化。”
下面的代码是非法的:
class Calendar implements Comparable<Calendar>{ ... }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>{...} //ERROR
GregorianCalendar会实现Comparable<Calender>和Compable<GregorianCalendar>,这是同一个接口的不同参数化实现。
这一限制与类型擦除的关系并不很明确。非泛型版本:
class Calendar implements Comparable{ ... }
class GregorianCalendar extends Calendar implements Comparable{...} //ERROR
是合法的。
有可能与合成的桥方法产生冲突有关:
public int compareTo(Object other) {return compareTo((X) other);}
方法体产生冲突,X应是Calender还是GregorianCalendar,不确定
八、泛型中 参数化类型不考虑继承关系
ArrayList<String> arrayList1=new ArrayList<Object>();//编译错误
ArrayList<Object> arrayList1=new ArrayList<String>();//编译错误
分享到:
相关推荐
4. **利用`Class<T>`作为泛型约束**:有时候,我们可以在泛型类中使用`Class<T>`作为参数或字段,这样可以在运行时获取类型信息。例如: ```java public class MyClass<T> { private final Class<T> clazz; ...
Java程序设计范型和枚举是Java编程中的关键概念,它们极大地增强了代码的类型安全性和重用性。本文将深入探讨这两个主题。 首先,我们来看什么是范型(Generics)。范型是Java SE 5.0引入的一个特性,它允许在类、...
Java范型是Java编程语言中的一个重要特性,它允许在类、接口和方法中使用类型参数,从而提高了代码的重用性和安全性。范型在Java中引入的主要目标是增强类型安全,减少强制类型转换,并帮助开发者编写更清晰、更易于...
这些范型定义了编程语言的基本结构和约束,影响着代码的编写方式以及解决问题的思路。本篇文章将深入探讨几种主要的程序设计语言范型,并提供一个概述。 1. **过程式编程**:这是最早期的编程范型,其核心思想是...
我将介绍类型约束、范型类、方法、结构和即将问世的范型类库。编译器如何处理范型?C++模板和Java语言中提议的范型等效都是它们各自编译器的功能。这些编译器在编译时根据对范型或模板类型的引用来构造代码。这会...
- **管理表、视图、索引、序列、约束**:这些都是数据库管理的重要组成部分,用于组织数据、提高查询效率以及维护数据完整性。 - **树状结构存储**:某些特定场景下,如组织结构或分类信息,使用树状结构进行存储更...
这个类可能有一个`T[]`类型的数组作为其内部数据结构,通过泛型约束确保插入和取出的元素都符合指定的类型。类中会包含上述提到的各种操作方法,如`add()`, `get()`, `remove()`, `set()`, `clear()`和`sort()`等。 ...
通过使用类型安全的异构容器设计模式,我们可以保留类型信息并在编译时强制执行类型约束,从而提高代码的可靠性和可维护性。不过,对于需要存储多种类型值的场景,需要额外的机制来保存和检查类型信息,如上述的...
在编程领域,尤其是在Java语言中,泛型是一种强大的特性,它允许...通过约束集合中的元素类型,我们可以编写出更加健壮且易于理解的代码。而标题中提到的电脑类,可能是在示例中用来演示泛型集合如何工作的具体实体。
范型(泛型) 泛型是Java中的一种类型参数化技术,使得编译器可以在运行时检查类型安全,并且所有的强制转换都是自动和隐式的。 - **声明**:在类、接口或方法中使用来指定类型参数。 - **实例化**:使用具体类型...
分布式通信范型如CORBA(Common Object Request Broker Architecture)和RMI(Remote Method Invocation)等,为Java提供了强大的企业级集成能力。 #### 4. 利用消息服务与JMS实现EAI JMS(Java Message Service)...
- 选择合适的开发范型:根据项目特性和需求,选择如瀑布模型、迭代模型、敏捷开发等方法。 - 应用适当的设计方法:使用结构化、面向对象或面向服务等设计策略,确保软件的可读性和可维护性。 - 提供高质量的工程...