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

不能用类型参数代替基本类型:例如,没有Pair,只有Pair,其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。这体现了Java语言中基本类型的独立状态。

运行时类型查询只适用于原始类型(raw type)

运行时:通常指在Classloader装载之后,JVM执行之时

类型查询:instanceof、getClass、强制类型转换

原始类型:即(raw type),泛型类型经编译器类型擦除后是Object或泛型参数的限定类型(例如Pair,Comparable就是T的限定类型,转化后泛型的原始类型就是Comparable,所以Pair类不带泛型是Pair),即Pair类含有Comparable类型的域

JVM中没有泛型

if(a instanceof Pair<String>) //ERROR,仅测试了a是否是任意类型的一个Pair,会看到编译器ERROR警告if(a instanceof Pair<T>) //ERRORPair<String> p = (Pair<String>) a;//WARNING,仅测试a是否是一个PairPair<String> stringPair = ...;
Pair<Employee> employeePair = ...;
if(stringPair.getClass() == employeePair.getClass()) //会得到true,因为两次调用getClass都将返回Pair.class//加入Java开发交流君样:756584822一起吹水聊天

不能创建参数化类型的数组(泛型数组)

参数化类型的数组:指类型带有泛型参数的数组,也即泛型数组,如Pair[] 、 T[]

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

Pair<String> table = new Pair<String>[10]; //ERROR

在这里我们假设可以实例化,那么经编译器类型擦除后,table的类型是Pair[],我们再让它协变为Object[]:

Object[] objArray = table;

而一般来说,数组会记住他的元素类型Pair,我们如果试图存储其他类型的元素,就会抛出异常(数组存储检查),例如:

objArray[0] = "Hello"; //ERROR--component type is Pair

但是,对于泛型类型Pair,类型擦除会使这种不同类检查机制无效,这就是不能实例化泛型数组的原因!

objArray[0] = new Pair<Employee>();
//如果泛型机制允许我们实例化数组,那么这一步就没理由出错了!
//而这违背了我们的初衷(限定类型)

数组存储只会检查擦除后的类型,又因为Java语言设计数组可以协变,所以可以通过编译
能够通过数组存储检查,不过仍会导致一个类型错误,故不允许创建参数化类型的数组
注意,声明类型为Pair[]的变量是合法的,只是不能创建这些实例(我们应该直接用new Pair[10]{…}来初始化这个变量)

泛型数组的间接实现:

通过泛型数组包装器,如ArrayList类,维护一个Object数组,然后通过进出口方法set、get来限定类型和强制转换数组类型,从而间接实现泛型数组,

例如:ArrayList: ArrayList<Pair<T>>、ArrayList<T>

不能实例化类型变量T

即不能使用new T(..) , new T[..] 或 T.class这样的表达式中的类型变量
例如: public Pair() { first = new T(); } //ERROR!类型擦除将T改变成Object,调用非本意的new Object()
不能使用new T(..)
但是,可通过反射调用Class.newInstance方法来构造泛型对象(要注意表达式T.class是非法的)

public static <T> Pair<T> makePair(Class<T> cl){try{ return new Pair<>(cl.newInstance() , cl.newInstance()); }catch(Exception ex) { return null; }
}
//加入Java开发交流君样:756584822一起吹水聊天
//这个方法可以按照下列方式调用:
Pair<String> p = Pair.makePair(String.class);

注意:Class类本身是泛型。String.class是一个Class的实例,因此makePair方法能够推断出pair的类型
不能使用new T[…]

解决方案:使用泛型数组包装器,例如ArrayList
然而,当在设计一个泛型数组包装器时,例如方法minmax返回一个T[]数组,则泛型数组包装器无法施展,因为类型擦除,return (T [])new Object是没有意义的强转不了。此时只好利用反射,调用Array.newInstance

import java.lang.reflect.*;
...
public static <T extends Comparable> T[] minmax(T... a){T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType() , 2);
...
}

【API文档描述】public Class<?> getComponentType() 返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。
而ArrayList类中的toArray方法的实现就麻烦了


public Object[] toArray() 无参,返回Object[]数组即可 
public Object[] toArray() {return Arrays.copyOf(elementData, size);}

【API文档描述】public static T[] copyOf(T[] original,int newLength)
  复制指定的数组,截取或用 null 填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含 null。当且仅当指定长度大于原数组的长度时,这些索引存在。所得数组和原数组属于完全相同的类。
public T[] toArray(T[] a) a - 要存储列表元素的T[]数组(如果它足够大)否则分配一个具有相同运行时类型的新数组,返回该T[]数组

@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass()); //a.getClass()得运行时目的数组的运行时类型//加入Java开发交流君样:756584822一起吹水聊天System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}

【API文档描述】
public static <T,U> T[] copyOf(U[] original,int newLength, Class<? extends T[]> newType)
复制指定的数组,截取或用 null 填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含 null。当且仅当指定长度大于原数组的长度时,这些索引存在。所得数组属于 newType 类。
泛型类的静态上下文中类型变量无效

泛型类不能在静态域或静态方法中引用类型变量

public class Singleton<T>{private static T singleInstance; //ERRORpublic static T getSingleInstance(){...} //ERROR
}

类型擦除后只剩下Singleton类,因为静态所以他只包含一个singleInstance域,如果能运行则以Singleton类为模板生成不同类型的域,因此产生了冲突

不能throws或catch泛型类的实例(有关异常)

泛型类继承Throwable类不合法,如public class Problem<T> extends Exception {...}//ERROR 不能通过编译
catch子句不能使用类型变量

public static <T extends Throwable> void doWork(Class<T> t){try{do work}catch (T e){ // ERRORLogger.global.info(...)}
}

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

public static <T extends Throwable> void doWork(T t) throws T { //此时可以throws Ttry{//加入Java开发交流君样:756584822一起吹水聊天do work}catch (Throwable realCause){ //捕获到具体实例t.initCause(realCause); throw t; //这时候抛具体实例,所以throw t 和 throws T 是可以的!}
}

此特性作用:可以利用泛型类、类型擦除、SuppressWarnings标注,来消除对已检查(checked)异常的检查,
unchecked和checked异常: Java语言规范将派生于Error类或RuntimeException的所有异常称为未检查(unchecked)异常,其他的是已检查(checked)异常

  • Java异常处理原则:必须为所有已检查(checked)异常提供一个处理器,即一对一个,多对多个
 @SuppressWarnings("unchecked") //SuppressWarning标注很关键,使得编译器认为T是unchecked异常从而不强迫为每一个异常提供处理器
public static <T extends Throwable> void throwAs(Throwable e) throwsT{  //因为泛型和类型擦除,可以传递任意checked异常,例如RuntimeException类异常throw (T) e;
}

假设该方法放在类Block中,如果调用 Block.throwAs(t); 编译器就会认为t是一个未检查的异常

public abstract class Block{public abstract void body() throws Exception;public Thread toThread(){return new Thread(){public void run(){try{body();}catch(Throwable t){Block.<RuntimeException>throwAs(t);}//加入Java开发交流君样:756584822一起吹水聊天}};}@SuppressWarnings("unchecked")public static <T extends Throwable> void throwAs(Throwable e) throws T{throw (T) e ;}
}

再写个测试类

public class Test{public static void main(String[] args){new Block(){public void body() throws Exception{//不存在ixenos文件将产生IOException,checked异常!Scanner in = new Scanner(new File("ixenos"));while(in.hasNext())System.out.println(in.next());}//加入Java开发交流君样:756584822一起吹水聊天}.toThread().start();}
}
  • 启动线程后,throwAs方法将捕获线程run方法所有checked异常,“处理”成unchecked
    Exception(其实只是骗了编译器)后抛出;

有什么意义?正常情况下,因为run()方法声明为不抛出任何checked异常,所以必须捕获所有checked异常并“包装”到未检查的异常中;意义:而我们这样处理后,就不必去捕获所有并包装到unchecked异常中,我们只是抛出异常并“哄骗”了编译器而已
注意擦除后的冲突

Java泛型规范有个原则:“要想支持擦除的转换,就需要强行限制一个泛型类或类型变量T不能同时成为两个接口类型的子类,而这两个接口是统一接口的不同参数化”
注意:非泛型类可以同时实现同一接口,毕竟没有泛型,很好处理

class Calender implements Comparable<Calender>{...}class GGCalender extends Calender implements Comparable<GGCalender>{...} //ERROR

在这里GGCalender类会同时实现Comparable 和 Comparable,这是同一接口的不同参数化

Java 泛型 泛型的约束与局限性相关推荐

  1. java泛型程序设计——约束与局限性

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java泛型程序设计 的 约束与局限性 的知识: [1] 不能用基本类型实例化类型参数 1.1 ...

  2. java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  3. java使用泛型后消除泛型_如何以及何时使用泛型

    java使用泛型后消除泛型 本文是我们名为" 高级Java "的学院课程的一部分. 本课程旨在帮助您最有效地使用Java. 它讨论了高级主题,包括对象创建,并发,序列化,反射等. ...

  4. 【Java】泛型中 extends 和 super 的区别?

    <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...

  5. 聊聊Java的泛型及实现

    泛型基础 泛型是对Java语言类型系统的一种扩展,有点类似于C++的模板,可以把类型参数看作是使用参数化类型时指定的类型的一个占位符.引入泛型,是对Java语言一个较大的功能增强,带来了很多的好处: ...

  6. java gt_JAVA泛型知识--gt; lt;? extends Tgt;和lt;? super Tgt;

    extends T> 和 super T> 是Java泛型中的"通配符(Wildcards)" 和 "边界(Bounds)"的概念 extends ...

  7. 六、JAVA基础--泛型

    泛型方法:泛型方法可以放在普通类中,也可以定义在泛型类中. <T extends 具体类或者接口> T为绑定类型的子类型:T和绑定类型可以是类,也可以是接口. 可以有多个限定类型,用'&a ...

  8. java心得---泛型

    1.集合类的泛型 泛型是JDK1.5后出现的一个安全机制.通过< >来指定容器中元素的类型 好处:1.将运行时期出现的问题转移到编译时期. 2.避免了强制类型转换的麻烦 2. 泛型的思想 ...

  9. 002 Java集合泛型面试题

    Java集合/泛型面试题 1 ArrayList和linkedList的区别 ArrayList: 可以看作是能够自动增长容量的数组 ArrayList底层的实现是Array, 数组扩容实现 Arra ...

  10. 韩顺平Java学习 泛型

    目录 一.泛型的语法 1.传统的方法遍历集合存在的问题 2.使用泛型来解决传统集合添加元素带来的问题 3.泛型使用介绍 4.泛型的使用细节 5.泛型课堂练习 二.自定义泛型 1.自定义泛型类的规范 ​ ...

最新文章

  1. oracle字符界面安装,怎么解决oracle在linux 纯字符界面安装有关问题
  2. 平方的观测值表概率_中央气象台:“三九”大概率不会比“二九”更冷
  3. Asp.Net Core 第02局:Program
  4. 匈牙利算法-指派问题、二分图问题等
  5. 测试-LoadRunner
  6. navicat导入CSV/Excel文件
  7. android中OpenMax的实现【2】AwesomePlayer中openmax的入口
  8. 令人纠结的表单设计:水平选项 Or 垂直选项?
  9. 计算机管理无法输入密码,光大网银控件已安装但无法输入密码
  10. Android拍照识别身份证SDK
  11. 桑基图(Echarts)——自定义风格
  12. 圣诞树的雪花飘飘(结尾附源码)
  13. Geogebra的使用
  14. C#和西门子PLC使用Udp通信
  15. 金钱不能买什么读后感_金钱的魔力读后感
  16. vue3.0中找不到组件 或者找不到对应的类型声明。怎么解决
  17. CVPR 2022 | QueryDet:使用级联稀疏query加速高分辨率下的小目标检测
  18. 五分钟用 linux搭建我的世界服务器
  19. Windows7声卡驱动不行怎么办
  20. 用于多核DSP开发的核间通信

热门文章

  1. .NET IdentityServer4实战-开篇介绍与规划
  2. 助力苏州、星火相传,广苏两地微软技术俱乐部交流纪实
  3. 从.Net框架Bug的提交到修复代码成功合并到.NET CoreFX主线
  4. 入门干货之Electron的.NET实现-Electron.NET
  5. C#使用Xamarin开发可移植移动应用(3.Xamarin.Views控件)附源码
  6. .NET 的一点历史往事:和 Java 的恩怨
  7. Visual Studio 2017 ASP.NET Core开发
  8. ntop linux,Linux下开源监控软件Ntop的性能提升方案
  9. ArcGIS实验教程——实验三十九:ArcGIS多元分类(ISO聚类分析、最大似然分类、主成分分析)案例教程
  10. linux shell之控制台打印各种颜色字体和背景