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) //ERROR,仅测试了a是否是任意类型的一个Pair,会看到编译器ERROR警告

if(a instanceof Pair) //ERROR

Pair p = (Pair) a;//WARNING,仅测试a是否是一个Pair

Pair stringPair = …;

Pair employeePair = …;

if(stringPair.getClass() == employeePair.getClass())

//会得到true,因为两次调用getClass都将返回Pair.class

//加入Java开发交流君样:756584822一起吹水聊天

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

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

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

Pair table = new Pair[10]; //ERROR

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

Object[] objArray = table;

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

objArray[0] = “Hello”; //ERROR–component type is Pair

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

objArray[0] = new Pair();

//如果泛型机制允许我们实例化数组,那么这一步就没理由出错了!

//而这违背了我们的初衷(限定类型)

数组存储只会检查擦除后的类型,又因为Java语言设计数组可以协变,所以可以通过编译

能够通过数组存储检查,不过仍会导致一个类型错误,故不允许创建参数化类型的数组

注意,声明类型为Pair[]的变量是合法的,只是不能创建这些实例(我们应该直接用new Pair[10]{…}来初始化这个变量)

泛型数组的间接实现:

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

例如:ArrayList: ArrayList<Pair>、ArrayList

不能实例化类型变量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 Pair makePair(Class cl){

try{ return new Pair<>(cl.newInstance() , cl.newInstance()); }

catch(Exception ex) { return null; }

}

//加入Java开发交流君样:756584822一起吹水聊天

//这个方法可以按照下列方式调用:

Pair 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[] 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[] 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{

private static T singleInstance; //ERROR

public static T getSingleInstance(){…} //ERROR

}

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

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

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

catch子句不能使用类型变量

public static void doWork(Class t){

try{

do work

}catch (T e){ // ERROR

Logger.global.info(…)

}

}

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

public static void doWork(T t) throws T { //此时可以throws T

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
mg-community.csdnimg.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

最后总结

搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析

最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-NNAHt9jN-1713113478562)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值