java - Java对参数化类型的原始引用不尊重参数
问题描述
我正在尝试各种实例化 HashMap 的方法,但我遇到了一个问题。在创建从 String 到 String 的映射时,可以在以下选项中进行选择:
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map = new HashMap<>();
Map<String, String> map = new HashMap();
我认为使用第一个的好处是将内存中的整个对象绑定到键和值的 String 类型,因为 reference(map) 将指向 HashMap<String, String> 类型的实际对象
运行以下代码不会引发错误并显示合理的输出:
Map<String, String> map = new HashMap<String, String>();
map.put("hi", "there");
Map map2 = map;
map2.put(5, 4);
System.out.println(map.get("hi"));
System.out.println(map2.get("hi"));
System.out.println(map2.get(5));
System.out.println(map2.get(5).getClass());
System.out.println(map.size());
输出是:
there
there
4
class java.lang.Integer
2
我们可以清楚地看到两个引用都指向内存中的同一个对象,我认为它的类型应该是 HashMap<String, String>,但是当引用未参数化时,我们可以添加 Object 的任何子类型。
我的问题是,既然参数化构造函数对于存储在内存中的对象无关紧要,我们为什么要重复右侧的类型呢?为什么要写这个:
Map<String, String> map = new HashMap<String, String>();
对此:
Map<String, String> map = new HashMap();
即使它具有相同的效果?
解决方案
由于类型擦除,生成的代码没有区别。泛型所要做的就是获得编译器关于代码正确性的反馈。只有当您的代码没有“未检查”和“原始类型”警告时,您才能确定不会发生“堆污染”,即存储的对象类型不正确。
对此,行
Map<String, String> map = new HashMap();
是有问题的,因为它会生成编译器警告。当然,当你看到它并对他的含义有足够的了解时,你就会知道它不会造成任何伤害。
相比之下,类似的线
Map<String, String> map = new HashMap(oldMap);
确实允许使用不正确的内容初始化地图。当然,您现在可以检查 的类型oldMap
,看看它是否兼容,并再次确信这种特定情况是可以的。但是这种方法违背了泛型的全部目的,让编译器为您检查这一点,您可以通过插入<>
.
一般来说,当你有一个大的代码库时,你希望它完全没有警告,所以你不必去每个位置,看看它是否可以被认为是无害的,即使它只需要看一眼。
插入的必要性<>
与历史有关。当 Java 5 发布时,所有方法调用的默认值都是执行泛型检查,因此说“兼容性问题”是使原始类型成为构造函数调用的默认值的原因并不令人信服。我的猜测是,javac
开发人员在类型推断的实现方面遇到了困难,并且存在发布时间压力。
请注意,对于 Java 5 和 Java 6,开发人员甚至必须为每个泛型实例显式插入完整类型。
在 Java 7 中,通过引入“菱形运算符”来缓解这个问题<>
,让编译器推断出泛型类型,就像它一开始就应该做的那样。但是,为了“兼容性”,仍然需要明确地请求推理,尽管如前所述,对于方法调用,没有选择加入机制,并且它从泛型的第一天开始就起作用。对我来说,原始类型默认是一个设计错误。
但是,出于实际目的,如上所述,您希望使用编译器执行检查的无警告应用程序,因此,您应该始终插入必要的<>
.
从 Java 10 开始,您可以声明局部变量,例如
var map = new HashMap<String, String>();
消除声明中的所有冗余。
推荐阅读
- vue.js - Vuetify 字体在生产和开发中是不同的
- php - 无论如何在laravel视图中使用JSON数据实现过滤?
- java - 是否可以使用 Realm 公开光标?
- python - 在 Python 中选择 MySQL 数据库
- android - 如何通过终端添加具有不同颁发者证书的新密钥库别名?
- python - 错误:无法打开包含文件:'unicode / ucsdet.h':没有这样的文件或目录
- c++ - 返回图的连接部分(dfs & graphs)
- javascript - MediaDevice API 约束:流已关闭
- qt - 在其他 QThread 中创建的 QThread 未调用析构函数
- c# - 有没有办法在不知道部分名称的情况下读取配置部分?(还有更多)