`
trophy
  • 浏览: 178635 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

java 范型 约束

    博客分类:
  • java
阅读更多

 

转自:

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>();//编译错误

 

 

 

 

 

分享到:
评论

相关推荐

    java 泛型类的类型识别示例

    4. **利用`Class&lt;T&gt;`作为泛型约束**:有时候,我们可以在泛型类中使用`Class&lt;T&gt;`作为参数或字段,这样可以在运行时获取类型信息。例如: ```java public class MyClass&lt;T&gt; { private final Class&lt;T&gt; clazz; ...

    Java程序设计范型和枚举PPT教案学习.pptx

    Java程序设计范型和枚举是Java编程中的关键概念,它们极大地增强了代码的类型安全性和重用性。本文将深入探讨这两个主题。 首先,我们来看什么是范型(Generics)。范型是Java SE 5.0引入的一个特性,它允许在类、...

    一个很好的范型立例题

    Java范型是Java编程语言中的一个重要特性,它允许在类、接口和方法中使用类型参数,从而提高了代码的重用性和安全性。范型在Java中引入的主要目标是增强类型安全,减少强制类型转换,并帮助开发者编写更清晰、更易于...

    主要程序设计语言范型综论与概要

    这些范型定义了编程语言的基本结构和约束,影响着代码的编写方式以及解决问题的思路。本篇文章将深入探讨几种主要的程序设计语言范型,并提供一个概述。 1. **过程式编程**:这是最早期的编程范型,其核心思想是...

    CLR中的范型详解

    我将介绍类型约束、范型类、方法、结构和即将问世的范型类库。编译器如何处理范型?C++模板和Java语言中提议的范型等效都是它们各自编译器的功能。这些编译器在编译时根据对范型或模板类型的引用来构造代码。这会...

    java知识点汇总学习路线与笔记

    - **管理表、视图、索引、序列、约束**:这些都是数据库管理的重要组成部分,用于组织数据、提高查询效率以及维护数据完整性。 - **树状结构存储**:某些特定场景下,如组织结构或分类信息,使用树状结构进行存储更...

    泛型自定义数组大小

    这个类可能有一个`T[]`类型的数组作为其内部数据结构,通过泛型约束确保插入和取出的元素都符合指定的类型。类中会包含上述提到的各种操作方法,如`add()`, `get()`, `remove()`, `set()`, `clear()`和`sort()`等。 ...

    如何使用Java泛型映射不同的值类型

    通过使用类型安全的异构容器设计模式,我们可以保留类型信息并在编译时强制执行类型约束,从而提高代码的可靠性和可维护性。不过,对于需要存储多种类型值的场景,需要额外的机制来保存和检查类型信息,如上述的...

    泛型集合的简单应用(电脑)

    在编程领域,尤其是在Java语言中,泛型是一种强大的特性,它允许...通过约束集合中的元素类型,我们可以编写出更加健壮且易于理解的代码。而标题中提到的电脑类,可能是在示例中用来演示泛型集合如何工作的具体实体。

    2022年牛客网-后端开发笔试题目

    范型(泛型) 泛型是Java中的一种类型参数化技术,使得编译器可以在运行时检查类型安全,并且所有的强制转换都是自动和隐式的。 - **声明**:在类、接口或方法中使用来指定类型参数。 - **实例化**:使用具体类型...

    希赛软考学院系统分析师考试辅导与培训_新技术应用资料

    分布式通信范型如CORBA(Common Object Request Broker Architecture)和RMI(Remote Method Invocation)等,为Java提供了强大的企业级集成能力。 #### 4. 利用消息服务与JMS实现EAI JMS(Java Message Service)...

    软件工程考研复试题.doc

    - 选择合适的开发范型:根据项目特性和需求,选择如瀑布模型、迭代模型、敏捷开发等方法。 - 应用适当的设计方法:使用结构化、面向对象或面向服务等设计策略,确保软件的可读性和可维护性。 - 提供高质量的工程...

Global site tag (gtag.js) - Google Analytics