Java泛型解析(04):约束和局限性
     
    前两节。认识和学习了泛型的限定以及通配符。刚開始学习的人可能须要一些时间去体会到泛型程序设计的优点和力量,特别是想成为库程序猿的同学就须要下去体会通配符的运用了,应用程序猿则须要掌握怎么使用泛型,这里针对泛型的使用中的约束和局限性做一个介绍性的解说。

不能用基本类型实例化类型參数
     这个非常好理解,实例化泛型类时泛型參数不能使用基本类型。如List<int>这是不合法的,要存储基本类型则能够利用基本类型的包装类如List<Integer> 、List<Double>等。以下补充一下八种基本类型相应的包装类。和默认初始值
[code01]:

          byte                 Byte            0
short Short 0
int Integer 0
long Long 0L
float FLoat 0.0F
double Double 0.0D
char Character '\u0000'
boolean Boolean false
不能实例化类型參数
     Java中创建对象使用newkeyword,可是泛型的类型參数不能用于实例化对象。如:
[code02]

     public Couple() {wife = new T(); husband = new T();} // 错误
     假设是在想要创建T对象。就必须使用Java反射来创建对象了,遗憾的是T.class在Java中也是不被支持使用的,所以一种弥补的方式。传入一个类型阐述为T的Class对象,如Class<T>
[code03]

           public static <T> Couple<T> createInstance(Class<T> clazz) {
try {
return new Couple<T>(clazz.newInstance(), clazz.newInstance());
} catch (Exception e) {
return null ;
}
}
     刚開始学习的人对Java反射不熟悉不用着急。这里仅仅要知道不能实例化类型參数就可以,同理,不能实例化一个泛型数组。如
[code04]

     public static <T> T[] maxTwo(T[] values) {T[] array = new T[2];} // 错误
     泛型构建数组是不合法的,由于这么构建在擦除之后构造的永远是new Object[2],这不是我们所希望的结果。

并且这样会导致一些执行时错误。

为了更进一步说明隐患问题,来看看以下代码:

[code05]

     public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
Object[] result = new Object[2];
return (T[]) result; // Type safety: Unchecked cast from Object[] to T[]
}
     这样的方式会产生变异警告:Object[]转换为T[]是没有被检查的。我们来试一试这个调用: maxTwo(new String[] { "5", "7" , "9" });。执行后,发生了类型转换异常。由于方法在调用的时候将Object[]转换为String[],失败的类型转化。

怎么解决呢?相同这里能够使用Java发射来解决:

[code06]

     public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
// Type safety: Unchecked cast from Object[] to T[]
return (T[]) Array.newInstance(array.getClass().getComponentType(), 2) ;
}
不能声明參数化的数组
     Couple<Employee>[] couple = new Couple<Employee>[5] ;这样的声明式不合法的,这里面有一个问题还是通过类型擦除机制来解释,类型擦除后couple的类型是Couple[]。考虑一下两种赋值方式:
     1.couple[0] = "wife"时,编译器会报错,这个非常明显的错误。couple每一个元素都是Couple类型。
     2.couple[0] = new Couple<String>()。类型擦除后这个能够通过数组检測,但仍然是错误的类型,由于couple在声明的时候定义的是Couple<Employee>。所以会出现故障。
     假设要存放參数化类型对象的集合,能够考虑使用ArrayList<Couple<Employee>>进行声明,并且Java中建议优先使用集合。这样既安全又有效。

类型參数不能进行类型查询
     通常我们会使用if (arg1 instanceof Couple)来推断arg1是否属于Couple类型,有些爱思考的程序猿可能会想更详细地去推断arg1类型是属于Couple<T>。指定类型參数,如:
[code07]

     if (arg1 instanceof Couple<Employee>){...} // 错误
     Java虚拟机中没有泛型概念,全部instanceof 类型查询仅仅查询原始类型。所以code07中的语法在Java中的不支持的。同理,在强制类型转换中也是不同意指定类型參数的。如:Couple<Employee> couple =
(Couple<Employee>)arg1; // 错误
不能抛出、不能捕获泛型类实例
     在Java中。public class GenericException <T> extends Exception {...}这样的泛型类扩展子Throwable是不合法的,不能通过编译器。

     不能再catch子句中使用类型參数,如:
[code08]

        public static <T extends Throwable> void doSomething(Class<T> t) {
try {
// do something...
} catch (T e) {
e.printStackTrace();
}
} // 错误
     code08中的代码是不合法的,以下在异常声明中使用类型參数是合法的,比如:
[code09]

          public static <T extends Throwable> void doSomething(T t) throws T {
try {
// do something...
} catch (Throwable e) {
e.printStackTrace();
throw t;
}
}// 正确
泛型类中的类型參数不能用于静态上下文中
     怎么理解这个呢?看个样例就知道了,如一个泛型单例:
[code10]

public class Singleton <T>{

           private static T instance;

           public static T getInstance(){ ..}
}
     上述代码,是无法通过编译器的,怎么解释这个呢?试想如果这个程序能够执行。声明一个Singleton<String>和一个Singleton<Employee>。类型擦除之后都是Singleton。但仅仅包括了一个instance域。这个域不可能同一时候是String类型的又是Employee类型的。所以如果是不成立的。顺便回想了一下学校里学的反证法~
~

类型擦除后引起的冲突
     看一个泛型类:
[code11]

     public class NameClash<T> {
public boolean equals(T value) {
return false ;
}
}
     从这个类的定义中来看,存在两个equals方法,一个是自身定义的public boolean equals(T
value) {...}。一个是从Object继承的public boolean equals(Object obj) {...},但类型擦除以后,前者方法成为了public boolean equals(Object value) {...},而在一个类中同一时候存在两个方法名和參数一样的方法是不可能的,所以这里引发的冲突是没法通过编译器的。能够通过又一次命名方法进行修正。

     擦除引起的冲突还体如今还有一点上。再看一段错误的代码:
[code12]

     class Calendar implements Comparable<Calendar> {...}
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> {...}
     上述代码是非法的。为什么?回想一下类型擦除后,虚拟机会为Calendar 类合成桥方法,实现了Comparable<Calendar>获得一个桥方法:
     public int compareTo (Object
o) {return compareTo((Calendar)o);}
     而实现了Comparable<GregorianCalendar >在类型擦除后,虚拟机为GregorianCalendar合成一个桥方法:     
     public int compareTo (Object
o) {return compareTo((GregorianCalendar )o);}
     这样一来在GregorianCalendar类中存在两个一样的方法。这是不同意的。

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



=====【感谢亲阅读寻常心的文章,亲若认为此文有帮助。顶一顶亲的支持将给我前进的动力】=====

Java泛型解析(04):约束和局限性的更多相关文章

  1. Java泛型解析(03):虚拟机运行泛型代码

    Java泛型解析(03):虚拟机运行泛型代码      Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...

  2. Java泛型解析(01):认识泛型

    Java泛型解析(01):认识泛型 What      Java从1.0版本号到如今的8.中间Java5中发生了一个非常重要的变化,那就是泛型机制的引入.Java5引入了泛型,主要还是为了满足在199 ...

  3. Java泛型解析(02):通配符限定

    Java泛型解析(02):通配符限定      考虑一个这种场景.计算数组中的最大元素. [code01] public class ArrayUtil { public static <T&g ...

  4. Java泛型解析

    1. 概述    在引入范型之前,Java类型分为原始类型.复杂类型,其中复杂类型分为数组和类.引入范型后,一个复杂类型就可以在细分成更多的类型. 例如原先的类型List,现在在细分成List< ...

  5. 在Java泛型

    1,泛型的定义以及存在意义 泛型,即"参数化类型".就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传 ...

  6. Java 泛型 泛型的约束与局限性

    Java 泛型 泛型的约束与局限性 @author ixenos 不能用基本类型实例化类型参数 不能用类型参数代替基本类型:例如,没有Pair<double>,只有Pair<Doub ...

  7. Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  8. 解析令人费解的java泛型

    对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念.主要内容 ...

  9. 解析java泛型(一)

    对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念.主要内容 ...

随机推荐

  1. django+nginx+uwsgi 项目部署

    Django虽然自带一个Server,但只能作为开发时测试使用,我们需要一个可以稳定而持续的服务器对网站进行部署,比如Apache, Nginx, lighttpd等,本篇将利用nginx和uWSGI ...

  2. 算法笔记_094:蓝桥杯练习 矩阵相乘(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 小明最近在为线性代数而头疼,线性代数确实很抽象(也很无聊),可惜他的老师正在讲这矩阵乘法这一段内容. 当然,小明上课打瞌睡也没问题,但线性 ...

  3. SpringMVC响应Restful风格请求404

    在学习Springmvc时,使用Restful风格的url,页面提示404错误.为找到原因,编写一个简单的Restful测试用例如下: jsp页面: <a href="user/tes ...

  4. GUID转换成16位字符串或19位唯一字符串

    整理几个经常使用GUID转换成16位字符串或19位唯一字符串方法: /// <summary> /// 依据GUID获取16位的唯一字符串 /// Author : 付义方 /// < ...

  5. Hibernate 第一个体验程序

    首先要导入包,将下载的hibernate所有required包导入,将下载的hibernate用来写log的slf4j的api和nopjar包导入,将下载的mysql链接引擎jar包导入. 然后新建j ...

  6. top命令的Load average 含义及性能参考基值

    $ uptime11:12:26 up 3:44, 4 users, load average: 0.38, 0.31, 0.19 系统平均负载被定义为在特定时间间隔内运行队列中的平均进程树.如果一个 ...

  7. Linux-HUP信号的干扰问题

    在向大家详细介绍Linux HUP信号之前,首先让大家了解下Linux HUP信号,然后全面介绍Linux服务器X,希望对大家有用.想让进程在断开连接后依然保持运行?如果该进程已经开始运行了该如何补救 ...

  8. XML序列化 判断是否是手机 字符操作普通帮助类 验证数据帮助类 IO帮助类 c# Lambda操作类封装 C# -- 使用反射(Reflect)获取dll文件中的类型并调用方法 C# -- 文件的压缩与解压(GZipStream)

    XML序列化   #region 序列化 /// <summary> /// XML序列化 /// </summary> /// <param name="ob ...

  9. 各种MQTT server功能比較

    this page attempts to document the features that various MQTT servers (brokers) support. This is spe ...

  10. laravel的模板嵌套循环可以这样调试

    @foreach($first as $value) {{ dump($value) }} @endforeach