如何在没有明确说明通用类型的情况下滥用?

时间:2015-03-18 11:51:48

标签: java generics types

假设我有这段代码:

private Hashtable items = new Hashtable();

Eclipse会对此类赋值发出警告,因为它是一种未经检查,不安全的操作。

更好的解决方案是:

private Hashtable<String,String> items = new Hashtable<String,String>();

或(如果我不确定类型)

private Hashtable<?,?> items = new Hashtable<?,?>();

不明确提供类型的后果是什么?从安全角度来看,这可能会被滥用吗?这可以在测试中被捕获(我必须使用反射吗?)以及如何?

2 个答案:

答案 0 :(得分:1)

通过使用原始类型,您无法让编译器阻止您将任何旧键和值添加到地图中。如果您的密钥和值恰好是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.
}

关于如何在测试中捕获它的问题 - 你不应该为这类东西编写测试 - 成功的编译是你的测试!

编译时类型安全性给你的覆盖范围远远大于你实际希望编写的任何测试的代码大大减少。

答案 1 :(得分:0)

Java中的泛型是一个编译时概念,它们在运行时不存在。这称为类型擦除。因此,从安全的角度来看,它们不能在正在运行的应用程序中被滥用。 我不确切地知道你想在测试中用反射检查什么,但是你只能在少数情况下通过反射得到泛型信息。Reflection generics