没有明确声明泛型类型的赋值如何被滥用?
How can assignment without explicitly stated generic type be abused?
假设我有这个代码:
private Hashtable items = new Hashtable();
Eclipse 会针对此类赋值发出警告,因为这是未经检查的不安全操作。
更好的解决方案是:
private Hashtable<String,String> items = new Hashtable<String,String>();
或(如果我不确定类型)
private Hashtable<?,?> items = new Hashtable<?,?>();
不明确提供类型的后果是什么?从安全的角度来看,这会被滥用吗?这可以在测试中捕获吗(我必须使用反射吗?)以及如何捕获?
Java 中的泛型是一个编译时概念,它们在运行时不存在。这称为类型擦除。因此,从安全的角度来看,它们不能在 运行 应用程序中被滥用。
我不知道你想在测试中通过反射检查什么,但你只能在少数情况下通过反射获得泛型信息。Reflection generics
通过使用原始类型,您无法让编译器阻止您将任何旧键和值添加到映射中。如果您的键和值恰好是 Object
,那可能没问题,但根据我的经验,这不是很常见的事情。
通过使用显式类型边界,如果您尝试传入错误类型的参数,您的代码将无法编译。例如:
Map<String, String> genericMap = new HashMap<>();
genericMap.put(new Object(), new Object()); // compiler error.
但是,如果您要写:
Map rawMap = genericMap;
rawMap.put(new Object(), new Object()); // fine.
所以现在基本上所有关于该地图中包含的内容的赌注都没有了。如果您将 genericMap
传递给一个需要 Map<String, String>
的远程方法并且要从中获取值,您将遇到运行时失败,可能是 ClassCastException
:
import java.util.HashMap;
import java.util.Map;
class DontDoThis {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Map<String, String> genericMap = new HashMap<>();
Map rawMap = genericMap;
rawMap.put(new Object(), new Object());
useMap(genericMap);
}
private static void useMap(Map<String, String> map) {
for (String key : map.keySet()) {} // ClassCastException.
}
}
如果您使用通配符边界,您将无法执行诸如将新值放入地图中的操作:
Map<?, ?> wildcardMap = genericMap;
genericMap.put("key1", "value1"); // fine.
wildcardMap.put("key", "value"); // compiler error.
但是,您仍然可以使用 wildcardMap
中的值;关于它们,您所知道的只是这些值是 Object
:
的子类型
for (Map.Entry<?, ?> entry : wildcardMap.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
// Use key and value somehow.
}
关于如何在测试中捕获它的问题 - 你不应该为这种东西编写测试 - 成功编译就是你的测试!
编译时类型安全将为您提供的覆盖范围比您实际上希望编写的任何测试都要大得多,代码要少得多。
假设我有这个代码:
private Hashtable items = new Hashtable();
Eclipse 会针对此类赋值发出警告,因为这是未经检查的不安全操作。
更好的解决方案是:
private Hashtable<String,String> items = new Hashtable<String,String>();
或(如果我不确定类型)
private Hashtable<?,?> items = new Hashtable<?,?>();
不明确提供类型的后果是什么?从安全的角度来看,这会被滥用吗?这可以在测试中捕获吗(我必须使用反射吗?)以及如何捕获?
Java 中的泛型是一个编译时概念,它们在运行时不存在。这称为类型擦除。因此,从安全的角度来看,它们不能在 运行 应用程序中被滥用。 我不知道你想在测试中通过反射检查什么,但你只能在少数情况下通过反射获得泛型信息。Reflection generics
通过使用原始类型,您无法让编译器阻止您将任何旧键和值添加到映射中。如果您的键和值恰好是 Object
,那可能没问题,但根据我的经验,这不是很常见的事情。
通过使用显式类型边界,如果您尝试传入错误类型的参数,您的代码将无法编译。例如:
Map<String, String> genericMap = new HashMap<>();
genericMap.put(new Object(), new Object()); // compiler error.
但是,如果您要写:
Map rawMap = genericMap;
rawMap.put(new Object(), new Object()); // fine.
所以现在基本上所有关于该地图中包含的内容的赌注都没有了。如果您将 genericMap
传递给一个需要 Map<String, String>
的远程方法并且要从中获取值,您将遇到运行时失败,可能是 ClassCastException
:
import java.util.HashMap;
import java.util.Map;
class DontDoThis {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Map<String, String> genericMap = new HashMap<>();
Map rawMap = genericMap;
rawMap.put(new Object(), new Object());
useMap(genericMap);
}
private static void useMap(Map<String, String> map) {
for (String key : map.keySet()) {} // ClassCastException.
}
}
如果您使用通配符边界,您将无法执行诸如将新值放入地图中的操作:
Map<?, ?> wildcardMap = genericMap;
genericMap.put("key1", "value1"); // fine.
wildcardMap.put("key", "value"); // compiler error.
但是,您仍然可以使用 wildcardMap
中的值;关于它们,您所知道的只是这些值是 Object
:
for (Map.Entry<?, ?> entry : wildcardMap.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
// Use key and value somehow.
}
关于如何在测试中捕获它的问题 - 你不应该为这种东西编写测试 - 成功编译就是你的测试!
编译时类型安全将为您提供的覆盖范围比您实际上希望编写的任何测试都要大得多,代码要少得多。