-
KTV和泛型(2)
很多使用泛型的小伙伴,都会有一个疑惑:为什么有的方法返回值前带<T>、<K, V>之类的标记,而有的方法返回值前又什么都不带呢?就像这样:
// 实体基类 class Entity { public String toString() { return "Entity"; } } // 用户类 class User extends Entity { public String toString() { return "User"; } } // 用户Dao class UserDao { public String toString() { return "UserDao"; } } /** * 带<E>标记与不带<E>标记的比较 */ public class GenericsClass<T extends Entity> { // 不带标记 public void notSign(T t) { System.out.println("notSign " + t.toString()); } // 带<T>标记 public <T> void hasSign(T e) { System.out.println("hasSign " + e.toString()); } public static void main(String[] args) { GenericsClass<Entity> clazz = new GenericsClass<>(); Entity entity = new Entity(); User user = new User(); UserDao userDao = new UserDao(); System.out.println("不带标记的方法:"); clazz.notSign(entity); clazz.notSign(user); // 不能编译通过的 // 因为在GenericsClass<T extends Entity>中已经限定了全局的T为Entity及其子类, // 所以不能再加入UserDao; // clazz.notSign(userDao); System.out.println("带标记的方法:"); clazz.hasSign(entity); clazz.hasSign(user); // 带上前缀<E>,就是在告诉编译器:这是新指定的一个类型,代表该方法自己独有的某个类, // 跟GenericsClass<T extends Entity>中的Entity及其子类没有任何关系 // 或者说 // hasSign方法重新定义泛型T、隐藏或者代替了GenericsClass<T>中的T,不再受限于Entity及其子类 clazz.hasSign(userDao); } }
因此,返回值前面的<T>的作用是「重新定义泛型」,因此方法参数类型不受对象泛型类型的限制。在Java新的Stream API中有大量这种带前缀的用法,例如:
ArrayList.java:public <T> T[] toArray(T[] a) Optional.java:public static <T> Optional<T> of(T value) Collectors.java:public static <T> Collector<T, ?, List<T>> toList()
泛型,作为Java的一个基础特性,并不是一点毛病都没有,「泛型擦除」就是至今还未解决的一个问题(这个问题其实对于大多数人来说可以不用知道,因为实际应用中极少出现这种场景,感兴趣的话稍稍了解一下,不喜可绕过)。
所谓泛型擦除,是这样一个问题:
class User {} class Product {} class Shop<V> {} class Particle<PPP, QQQ> {} public class LostInformation<T> { public static void main(String[] args) { // ArrayList<String>和ArrayList<Integer>应该是不同的类型,但结果是它们完全相等 Class<?> c1 = new ArrayList<String>().getClass(); Class<?> c2 = new ArrayList<Integer>().getClass(); System.out.println(c1 == c2); // 无法从泛型获得任何有关参数类型的信息 List<User> list = new ArrayList<>(); Map<User, Product> map = new HashMap<>(); Shop<User> shop = new Shop<>(); Particle<Long, Double> p = new Particle<>(); System.out.println(Arrays.toString(list.getClass().getTypeParameters())); System.out.println(Arrays.toString(map.getClass().getTypeParameters())); System.out.println(Arrays.toString(shop.getClass().getTypeParameters())); System.out.println(Arrays.toString(p.getClass().getTypeParameters())); } }
可以看见,Particle<Long, Double>中关于Long和Double的信息已经完全丢失了,只剩下了最初的PPP和QQQ,真的应了那句话:历经千帆,归来仍是是少年!
但这是编程啊,不是文案啊~,不能这样丢掉了!
出处:https://www.cnblogs.com/xiangwang1111/p/16770391.html
最新更新
KTV和泛型(3)
KTV和泛型(2)
KTV和泛型(1)
Java中的抽象类与接口(2)
Java中的数据类型(3)
Java中的数据类型(2)
Java中的数据类型(1)
回调、异步和观察者一次分清
硬核剖析Java锁底层AQS源码,深入理解底层
每日算法题之二叉树的深度
三大常用数据库事务详解之三:事务运行
三大常用关系型数据库事务详解之二:基
三大关系型数据库事务详解之一:基本概
MongoDB常用命令(2)
MongoDB基本介绍与安装(1)
SQLServer触发器调用JavaWeb接口
SQL Server索引的原理深入解析
SqlServer2016模糊匹配的三种方式及效率问题
SQL中Truncate的用法
sqlserver 多表关联时在where语句中慎用tri
在vscode中使用R时,用快捷键来快捷键入卡
VB.NET中如何快速访问注册表
ASP.NET中图象处理过程详解
Vue(1)Vue安装与使用
JavaScript 语言入门
js将一段字符串的首字母转成大写
纯原生html编写的h5视频播放器
H5仿原生app短信验证码vue2.0组件附源码地
TypeScript(4)接口
TypeScript(3)基础类型