Java泛型05 : 泛型使用的8个限制

超级通道: Java泛型学习系列-绪论

本章主要对Java泛型使用的8个限制进行说明。

1.Java泛型不能使用基本类型

java的基本类型:boolean,char,float,double,byte,int,short,long。
使用基本类型的泛型会编译报错,代码如下:

List list = new List();// 编译前类型检查报错

分析:因为泛型在编译时,会进行类型擦除,最后只保留原始类型。而原始类型只能是Object类及其子类,当然不能使用基本数据类型。

2.Java泛型不允许进行实例化

错误代码如下:

<T> void test(T t){
    t = new T();//编译前类型检查报错
}

通过类型擦除,上面的泛型方法会转换为如下的原始方法:

void test(Object t){
	t = new Object();
}

而,实际使用中,实参t一般情况下都是Object的子类,此时t = new Object();相当于:

Integer t = 1;
t = new Object();

而Object肯定无法转换成Integer的。Java泛型为了防止此类类型转换错误的发生,禁止进行泛型实例化
当然,与类型擦除一样,可以通过Java反射实现泛型的实例化,但是不建议这么做。这里就不给出代码了,有兴趣的可以自己尝试。

3.Java泛型不允许进行静态化

参考下面的代码:

    /**
     * 

Title: 3.Java泛型不允许进行静态化

* @author 韩超 2018/2/23 11:25 */
static class Demo<T>{ private static T t;// 编译前类型检查报错 // 编译前类型检查报错 public static T getT() { return t; } }

原因:静态变量在类中共享,而泛型类型是不确定的,因此编译器无法确定要使用的类型,所以不允许进行静态化
备注:这里的静态化针对的对象是泛型变量,并不是泛型方法。感谢@ZYC_July的意见。

4.Java泛型不允许直接进行类型转换(通配符可以)

示例代码如下:

List<Integer> integerList = new ArrayList<Integer>();
List<Double> doubleList = new ArrayList<Double>();
//不能直接进行类型转换,类型检查报错
//integerList = doubleList;

我们已知,在编译阶段,两者都会经过类型擦除变为ArrayList,那为什么不能进行类型强制转换呢?
因为这违背了Java泛型设计的初衷:降低类型转换的安全隐患
integerList内存储的是Integer类型的元素,doubleList 是存储的是Double类型的元素,如果不限制类型转换,很容易产生ClassCastException异常。


Java泛型不允许直接进行类型转换,但是通过通配符可以实现类型转换(不建议这么做)
代码如下:

static void cast(List<?> orgin, List<?> dest){
    dest = orgin;
}
public static void main(String[] args){
    List<Integer> integerList = new ArrayList<Integer>();
    List<Double> doubleList = new ArrayList<Double>();
    //通过通配符进行类型转换
    cast(doubleList,integerList);
}

关于通配符的相关知识,会在下一章节解析学习。

5.Java泛型不允许直接使用instanceof运算符进行运行时类型检查(通配符可以)

直接使用instanceof运算符进行运行时类型检查:

List<String> stringList = new ArrayList<String >();
//不能直接使用instanceof,类型检查报错
//LOGGER.info(stringList instanceof ArrayList);

我们可以通过通配符的方式进行instanceof运行期检查(不建议)

//通过通配符实现运行时验证
LOGGER.info(stringList instanceof ArrayList<?>);

result:

2018-02-23 14:33:20 INFO  GenericAttentionDemo:71 - true

6.Java泛型不允许创建确切类型的泛型数组(通配符可以)

创建确切类型的泛型数组如下:

//类型检查报错
//Demo6[] iDemo6s = new Demo6[2];

我们可以通过通配符的方式创建泛型数组(不建议)

//通过通配符实现泛型数组
Demo6<?>[] iDemo6s = new Demo6<?>[2];
iDemo6s[0] = new Demo6<Integer>(1);
iDemo6s[1] = new Demo6<String >("hello");
for (Demo6 demo6 : iDemo6s){
    LOGGER.info(demo6.getT().getClass().toString() + " : " + demo6.getT());
}

reuslt:

2018-02-23 14:58:01 INFO  GenericAttentionDemo:97 - class java.lang.Integer : 1
2018-02-23 14:58:01 INFO  GenericAttentionDemo:97 - class java.lang.String : hello

7.Java泛型不允许定义泛型异常类或者catch异常(throws可以)

直接看代码:

/**
  * 

Title: 7.Java泛型不允许定义泛型异常类

* * @author 韩超 2018/2/23 15:07 */
// static class Apple extends Exception { // } // static class Orange extends Throwable { // } /** *

Title: 7.Java泛型不允许捕获一个泛型异常

* * @author 韩超 2018/2/23 15:09 */
//7.Java泛型:可以throws泛型类型 <T extends Exception> void test7(T t,Class<T> tClass) throws T { try { //... // } catch (T t) {//不允许捕获一个泛型类型的异常 } catch (Exception e) {//不允许捕获一个泛型类型的异常 //... } }

为什么Java泛型不允许定义泛型异常类?为什么不允许捕获一个泛型异常?先看下面的代码:

//假设可以定义泛型异常类
try{
	//。。。
}catch(MyException<Integer> e){
	//。。。
}catch(MyException<Double> e){
	//。。。
}

上面的代码,进行类型擦除时会去掉Integer和Double信息,形成如下代码:

try{
	//。。。
}catch(MyException e){
	//。。。
}catch(MyException e){
	//。。。
}

同时catch两个一样的异常,肯定不能通过编译。
Java泛型为了避免这种错误的产生,不允许定义泛型异常类或者catch异常。

总结:

  • Java泛型:不允许定义泛型异常类(直接或间接扩展Throwable类)
  • Java泛型:不允许捕获一个泛型异常
  • Java泛型:可以以异常类作为边界
  • Java泛型:可以throws泛型类型

8.Java泛型不允许作为参数进行重载

代码如下:

/**
 * 

Title: 8.Java泛型不允许作为参数进行重载

* @author 韩超 2018/2/23 15:32 */
static class Demo8<T>{ void test(List<Integer> list){} //不允许作为参数列表进行重载 //void test(List list){} }

为什么呢?
因为类型擦除之后,两个方法是一样的参数列表,这种情况无法重载。

9.总结

Java泛型其实还有其他的使用限制,这里不再赘述。
我们在理解Java泛型的使用限制时,应该首先用心理解下面两点:

  1. Java泛型的设计初衷:降低类型转换的安全隐患,而非为了实现任意化
  2. Java泛型的实现原理:类型擦除

你可能感兴趣的:(Java泛型,Java泛型学习实例)