【java基础】泛型的限制及其继承规则 落日映苍穹つ 2023-09-24 22:05 77阅读 0赞 ### 文章目录 ### * 说明 * 泛型的限制 * * 不能使用基本类型实例化参数 * 运行时类型查询只适用于原始类型 * 不能创建参数化类型数组 * Varargs警告 * 不能实例化类型变量 * 不能构造泛型数组 * 泛型类的静态上下文中类型变量无效 * 不能抛出或捕获泛型类的实例 * 可以取消对检查型异常的检查 * 关于擦除后的冲突 * 泛型的继承规则 * 总结 # 说明 # 在这篇文章中,将讨论在java中使用泛型时需要考虑的一些限制。大部分限制都是由类型擦除引起的。 -------------------- # 泛型的限制 # ## 不能使用基本类型实例化参数 ## 不能用基本类型代替类型参数。因此,没有<double>,只有<Object>。当然,其原因就在于类型擦除。擦除之后,类含有Object类型的字段,而Object不能存储double值。 这的确令人烦恼。但是,这样做与Java语言中基本类型的独立状态相一致。这并不是个致命的缺陷,java只有8种基本类型,而且即使不能接受包装器类型(wrapper type),也可以使用单独的类和方法来处理。 List<double> list; // ERROR List<Double> doubleList; // OK -------------------- ## 运行时类型查询只适用于原始类型 ## 我们都知道,在java中可以使用 instanceof来检查某个类是否类型相同或者为其子类。 String s = "x"; if (s instanceof Object) { System.out.println(s); } 但是不能够使用instanceof操作泛型。 泛型类 public class MyTool<T> { } 测试代码 MyTool<String> myTool = new MyTool<>(); if (myTool instanceof MyTool<String>){ // ERROR } if (myTool instanceof MyTool){ // Ok, Always True } 原因就是类型擦除,MyTool不同的泛型对象返回的class总是相同的,所有的MyTool对象的getClass都是调用的MyTool.class MyTool<Number> oMyTool = new MyTool<>(); MyTool<Integer> sMyTool = new MyTool<>(); // Always True System.out.println(oMyTool.getClass() == sMyTool.getClass()); -------------------- ## 不能创建参数化类型数组 ## public static void main(String[] args) { MyTool[] myTools = new MyTool[3]; // OK MyTool<String>[] myTools1; // OK 声明泛型数组没有问题 // ERROR 不能创建泛型数组 MyTool<String>[] myTools2 = new MyTool<String>[3]; } 如果一定要创建那么可以使用强制类型转换 MyTool[] myTools = new MyTool[3]; MyTool<Integer>[] myTools3 = (MyTool<Integer>[]) myTools; 这样写IDEA会报一个警告 ![在这里插入图片描述][5f1214bbcf4b4d64a5384be977538993.png] 这样写也是不安全的 MyTool[] myTools = new MyTool[3]; // myTools3执向myTools MyTool<Integer>[] myTools3 = (MyTool<Integer>[]) myTools; // 没有进行参数检查 myTools[0] = new MyTool<File>(); 编译器并不会对参数进行检查 -------------------- ## Varargs警告 ## 在上面,我们说了java不支持泛型类型的数组,但是,请看下面的代码 public static void main(String[] args) { List<MyTool<String>> list = new ArrayList<>(); MyTool<String> stringMyTool1 = new MyTool<>(); MyTool<String> stringMyTool2 = new MyTool<>(); addAll(list, stringMyTool1, stringMyTool2); } public static <T> void addAll(List<T> list, T... ts) { for (T t : ts) { list.add(t); } } 发现了什么?为了满足list的约束,ts不就必须是MyTool<String>的数组吗?这就和前面相违背了。对于这种情况,规则有所放松,只会得到一个警告,而不是错误 ![在这里插入图片描述][e63b892bf4b74b198a3f8fdd8262ed22.png] 我们可以使用@SafeVarargs表示在addAll方法上来忽略警告 **注意:@SafeVarargs只能用于static,final方法** -------------------- ## 不能实例化类型变量 ## public <T> void test(T t) { T newT = new T(); // ERROR } 对于类型变量,我们不能够使用new,上面代码IDEA会提示如下 ![在这里插入图片描述][b72656c5384a4bd4bbef5ad2064ef929.png] 表示不能实例化T。原因也很简单嘛,T被类型擦除后就变成Object了,new Object()有什么意义呢? -------------------- ## 不能构造泛型数组 ## 前面说了类型变量不能实例化,自然而然的也就不能够实例化泛型数组了 public <T> void test(T t) { T[] ts1; // 声明OK T[] ts2 = new T[3]; // 实例化ERROR } -------------------- ## 泛型类的静态上下文中类型变量无效 ## public class Tool<T> { public static T t; //ERROR public static T getT() { //ERROR return t; } } 这个很好理解,静态方法和静态字段是属于类的,当然无法得到泛型类的类型。 ## 不能抛出或捕获泛型类的实例 ## 既不能抛出也不能捕获泛型类的对象。实际上,泛型类根本就不能继承Throwable。 public class Test8<T> extends Throwable{ // ERROR } ![在这里插入图片描述][f15fa1cc75fc4320b3fc1b6b60337c99.png] 我们也不能使用泛型来进行catch public static <T extends Throwable> void test(T t) { try { int a = 1 / 0; } catch (T t1) { } } ![在这里插入图片描述][bd3f4226e4b9459e8fdfe1a185b4ff10.png] ## 可以取消对检查型异常的检查 ## 这个感觉没啥用,其实就是通过@SuppressWarnings、类型擦除和泛型类来哄骗编译器,我们抛出应该泛型的异常,让编译器认为这不是应该检查型异常 @SuppressWarnings("unchecked") public static <T extends Throwable> void throwAs(Throwable t) throws T { throw (T) t; } 上面代码就是,我们传入的可能是应该检查型异常,但是抛出的确实非检查型异常,这样就可以骗过编译器。 -------------------- ## 关于擦除后的冲突 ## 泛型规范说明还引用了另外一个原则:“为了支持擦除转换,我们要施加一个限制:倘若两个接口类型是同一接口的不同参数化,一个类或类型变量就不能同时作为这两个接口类型的子类。”例如,下述代码是非法的: > class Employee implements Comparable<Employee>\{…\} > class Manager extends Employee implements Comparable<Manager>\{…\} //ERROR Manager会实现Comparable<Employee>和Comparable<Manager>,这是同一接口的不同参数化。 这一限制与类型擦除的关系并不十分明显。毕竟,以下非泛型版本是合法的。 > class Employee implements Comparable \{…\} > class Manager extends Employee implements Comparable \{…\} 其原因非常微妙,有可能与合成的桥方法产生冲突。实现了Comparable<X>的类会获得一个桥方法: > public int compareTo(Object other)\{return compareTo((X)other); 不能对不同的类型X有两个这样的方法。 -------------------- # 泛型的继承规则 # 假设我们定义了应该泛型类Pair,Manager是Employee的子类,那么它们的继承关系如下 ![在这里插入图片描述][614cafda42ee42b9ab30c8b01e3d765f.png] 总结一下就是无论S与T有什么关系,通常,Pair<S>与Pair<S>都没有任何关系(如图8-1所示)。 还需要说明的是,泛型类可以扩展或实现其他的泛型类。就这一点而言,它们与普通的类没有什么 区别。 例如,ArrayList类实现了List接口。这意味着,一个ArrayList<Manager>可以转换为一个List<Manager>。但是,如前面所见,ArrayList<Manager>不是一个ArrayList<Employee>或List<Employee>。图8-2展示了它们之间的这种关系。 ![在这里插入图片描述][21fc533d11c04a6d93930fe53cd897b0.png] # 总结 # 对于上面的限制,大家不用全部背下来,再使用泛型的时候慢慢理解就好了 关于泛型的更多知识,参考以下内容 [**泛型程序设计基础**][Link 1] [**类型擦除、桥方法、泛型代码和虚拟机**][Link 2] [**泛型的限制及其继承规则**][Link 3] [**泛型的通配符(extends,super,?)**][extends_super] [5f1214bbcf4b4d64a5384be977538993.png]: https://img-blog.csdnimg.cn/5f1214bbcf4b4d64a5384be977538993.png [e63b892bf4b74b198a3f8fdd8262ed22.png]: https://img-blog.csdnimg.cn/e63b892bf4b74b198a3f8fdd8262ed22.png [b72656c5384a4bd4bbef5ad2064ef929.png]: https://img-blog.csdnimg.cn/b72656c5384a4bd4bbef5ad2064ef929.png [f15fa1cc75fc4320b3fc1b6b60337c99.png]: https://img-blog.csdnimg.cn/f15fa1cc75fc4320b3fc1b6b60337c99.png [bd3f4226e4b9459e8fdfe1a185b4ff10.png]: https://img-blog.csdnimg.cn/bd3f4226e4b9459e8fdfe1a185b4ff10.png [614cafda42ee42b9ab30c8b01e3d765f.png]: https://img-blog.csdnimg.cn/614cafda42ee42b9ab30c8b01e3d765f.png [21fc533d11c04a6d93930fe53cd897b0.png]: https://img-blog.csdnimg.cn/21fc533d11c04a6d93930fe53cd897b0.png [Link 1]: https://blog.csdn.net/m0_51545690/article/details/129354354 [Link 2]: https://blog.csdn.net/m0_51545690/article/details/129356404 [Link 3]: https://blog.csdn.net/m0_51545690/article/details/129360054 [extends_super]: https://blog.csdn.net/m0_51545690/article/details/129363949
相关 深入理解Java中的泛型及其限制 在Java中,泛型是一种类型参数化的机制,允许我们创建可以在多种数据类型的容器(如数组、列表、集合等)。 1. 泛型声明:在创建容器类时使用。例如: ```java List 系统管理员/ 2024年09月19日 17:54/ 0 赞/ 10 阅读
相关 深入理解Java中的泛型及其限制条件 在Java编程中,泛型是一种强大的工具,它允许我们创建能够存储任何类型对象的类或接口。泛型的主要概念包括类型参数、限制条件和通配符。 1. 类型参数(Type Paramet 矫情吗;*/ 2024年09月16日 13:57/ 0 赞/ 12 阅读
相关 Java泛型及其使用限制:示例对比解析 Java泛型是一种强大的工具,它允许我们在编写代码时声明类型,以避免运行时的类型转换错误。然而,泛型并非无限制,而是存在一定的使用限制和约束。 **1. 泛型参数** - 痛定思痛。/ 2024年09月10日 08:30/ 0 赞/ 14 阅读
相关 【java基础】泛型的限制及其继承规则 文章目录 说明 泛型的限制 不能使用基本类型实例化参数 运行时类型查询只适用于原始类型 不能创建参数化类型数组 落日映苍穹つ/ 2023年09月24日 22:05/ 0 赞/ 78 阅读
相关 JAVA 泛型类,限制泛型和泛型方法 我在学习集合的时候对泛型还不太了解,只是觉得用了泛型之后能向自己的集合里加入自己指定类型的对象,可以减少数据类型的转换。那么,脱离集合后,泛型到底该如何使用。今天碰巧看了 蔚落/ 2022年10月02日 12:45/ 0 赞/ 266 阅读
相关 Java基础-泛型 泛型的定义 1. 类或接口上定义泛型 class Demo<T>{ public void show(T t){ 超、凢脫俗/ 2022年03月26日 01:48/ 0 赞/ 268 阅读
相关 Java泛型继承 一 点睛 当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类来派生子类,但值得指出的是,当使用这些接口、父类时不能再包含类型形参。 如果使用泛型类时没有 谁践踏了优雅/ 2021年09月20日 12:32/ 0 赞/ 229 阅读
相关 Java泛型的继承和实现 一 点睛 泛型类和泛型接口被定义后,是可以被继承和实现的。 二 泛型类的继承 1 代码 class A<E> { E t; } ﹏ヽ暗。殇╰゛Y/ 2021年09月20日 09:34/ 0 赞/ 204 阅读
相关 Java泛型的使用限制 一 什么情况下不能使用泛型 1 不能使用泛型的形参创建对象。 T o=new T(); // 不允许 2 在泛型类中,不能给静态成员变量定义泛型 p 柔情只为你懂/ 2021年09月20日 09:32/ 0 赞/ 227 阅读
还没有评论,来说两句吧...