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

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

不能用类型参数代替基本类型:例如,没有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,这是同一接口的不同参数化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实施Exchange 2013中的 MailTip

实施Exchange 2013中的 MailTip邮件提示是用户撰写邮件时向其显示的提示性消息。Microsoft Exchange Server 2013 将分析邮件&#xff08;包括向其发送了邮件的收件人的列表&#xff09;&#xff0c;如果检测到潜在问题&#xff0c;它将使用邮件提示在邮件发送之前通知用户。借…

GitHub 的 Action 接入 Stryker.NET 进行自动化测试单元测试鲁棒性

假设有一个捣蛋的小伙伴加入了你的团队&#xff0c;这个捣蛋的小伙伴喜欢乱改代码&#xff0c;请问此时的单元测试能否拦住这些逗比行为&#xff1f;如果不能拦住逗比行为&#xff0c;是否代表着单元测试有所欠缺&#xff0c;或者有某些分支逻辑没有考虑到。本文将告诉大家的 S…

CN Erlounge IV 讲师名单公布及Call For Topic

近期将公布目前已经报名的讲师名单及Topic。那些希望Share Topic的朋友赶快了。 目前累计注册的讲师为12位&#xff08;不包括口头和我打过招呼但实际没有注册的讲师&#xff09;。 也提醒下那些已经注册但是Topic还是TODO的讲师赶紧提供下Topic。 :) 另外&#xff0c;借这个通…

防弹玻璃为啥会被钢球砸碎?这就是一道高中物理题!

全世界只有3.14 % 的人关注了青少年数学之旅马斯克&#xff0c;硅谷钢铁侠&#xff0c;全世界最具煽动力的企业家。旗下公司特斯拉最新电动皮卡&#xff0c;一经亮相就欢呼一片&#xff0c;传播到炸&#xff0c;看起来又要重新定义一个品类。然而也有网友“提醒”——如今的马斯…

八种ADSL接入情况中断流现象分析

转载自&#xff1a;网盟技术[url]http://technic.txwm.com[/url] 线路不稳定 如果住所离电信局太远(5公里以上)可以向电信部门申报。确保线路连接正确(不同的话音分离器的连接方法有所不同&#xff0c;请务必按照说明书指引正确连接)。同时确保线路通讯质量良好没有被干扰&…

Java之jdk与jre的区别

很多程序员已经干了一段时间java了依然不明白jdk与jre的区别。 JDK就是Java Development Kit.简单的说JDK是面向开发人员使用的SDK&#xff0c;它提供了Java的开发环境和运行环境。SDK是Software Development Kit 一般指软件开发包&#xff0c;可以包括函数库、编译程序等。 …

设计模式之组合

组合模式介绍一棵树结构组合模式是把相似对象或方法组合成一组可被调用的结构树对象的设计思路。组合模式不只是可以运用于规则决策树&#xff0c;还可以做服务包装将不同的接口进行组合配置&#xff0c;对外提供服务能力&#xff0c;减少开发成本。组合模式的主要解决的是一系…

实现 VC 最小化到 托盘

大家经常看到 程序 最小化到 托盘 但用C怎么实现呢 本人 试下哈 可以实现的 ~~ main.cpp 修改如下 #define WM_CLICKBIT (WM_USER 1) //定义消息 HINSTANCE hApp; NOTIFYICONDATA nid; BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(u…

最难面试IT公司的测试题

逛网站时无意间看到一道测试题&#xff0c;看到这标题我吓到了(最难面试IT公司)&#xff0c;本以为应该很难完成的&#xff0c;分析之后&#xff0c;花了半小时用python写出来了&#xff0c;各位朋友们也可以测试测试哦。不限语言。这是题目发布网址&#xff1a;https://www.ji…

leetcode中的状态机类型的题目

1 总结 一般是涉及到多个状态之间的转换&#xff0c;需要定义一个具有多个枚举值的变量&#xff0c;各个状态之间通过各种条件互相变化 2 LC57. 插入区间 2.1 解析 先是要确定新区间插入到哪一个位置&#xff08;也有可能&#xff09;&#xff0c;插入后需要确定这个区间是否…

如何用Python进行数据探索,探索竞赛优胜方案?

全世界只有3.14 % 的人关注了 青少年数学之旅 AI这个词相信大家都非常熟悉&#xff0c;近几年来人工智能圈子格外热闹&#xff0c;光是AlphoGo就让大家对它刮目相看。 随着大数据时代信息科技的快速发展&#xff0c;各种各样的数据充斥着我们的生活。而我们又当如何有效利用数据…

联想ThinkPad笔记本广告向苹果MacBook Air开战

联想ThinkPad笔记本广告向苹果MacBook Air开战 [url]http://www.sina.com.cn[/url] 2008年03月24日 07:21 新浪科技联想ThinkPad X300笔记本广告&#xff0c;暗讽苹果MacBook AirMacbook Air最厚的地方仅有1.94cm&#xff0c;边缘仅0.4cm视频&#xff1a;ThinkPad X300对比Mac…

你真的知道Java同步锁何时释放?

在测试java多线程中有关 “生产者和消费者” 这个经典问题的时候&#xff0c;写代码测试的时候&#xff0c;思考到一些问题&#xff08;所以还是要动手&#xff0c;实践才能储真知啊&#xff09;&#xff0c; synchronize 同步锁何时释放&#xff0c;何时获得&#xff1f;重新获…

基于 gRPC 和 .NET Core 的服务器流

原文&#xff1a;https://bit.ly/3lpz8Ll作者&#xff1a;Chandan Rauniyar翻译&#xff1a;精致码农-王亮早在 2019 年&#xff0c;我写过《用 Mapbox 绘制位置数据》一文&#xff0c;详细介绍了我如何通过简单的文件上传&#xff0c;用 Mapbox 绘制约 230 万个位置点。本文介…

jQuery入门[2]-选择器[转]

jQuery之所以令人爱不释手&#xff0c;在于其强大的选择器表达式令DOM操作优雅而艺术。jQuery的选择符支持id,tagName,css1-3 expressions,XPath&#xff0c;参见&#xff1a;http://docs.jquery.com/SelectorsDEMO:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran…

nmap配合shell使用

命令需求&#xff1a;查看X.X.X.X/22网段所有开启5900端口的服务器&#xff0c;并以IP/host分行列出nmap -p 5900 XX.XX.XX.XX/22 |sed -e /Host is up/d -e /STATE/d -e/^$/d |awk {print $2,$5,$6} |sed -n /^open/{g;1!p;};h|sed -es/scan//g>file#nmap -p用于扫描开启59…

是个狠角色。。 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源洱海弥&#xff0c;侵权删&#xff09;

构建Windows Server 2008虚拟实验室

在阅读新闻稿时&#xff0c;有很多文章和指导&#xff0c;都有一些帮助&#xff0c;但却没有针对Windows Server 2008的手把手经验可供参考。对管理员来说&#xff0c;要熟悉这个服务器系统的最好方法是确定它的特性是如何给他们的Windows环境带来便利。 先前我描述了如何方便地…

eclipse配置tomcat 和JRE环境

配置JRE环境&#xff0c;通俗点讲就是添加一个不同版本的jdk window——preferences——java——installed jres 点击add添加 选择standard VM 择一个本机正确的jdk路径 name和下面lib加载都是系统自动完成的 选择好路径后 点 finish就ok了 配置tomcat window——preferen…

为什么我的 Func 如此之慢?

咨询区 Ricky G&#xff1a;我正在给项目做性能和代码优化&#xff0c;比如将重复的代码提炼成到一个可重用的方法中&#xff0c;为了能够达到可重用目的&#xff0c;我用 Func<T> 作为方法参数。public int Calculate(Func<int> expr){return expr();}当我用 Benc…