帮别人的代码改bug,发现有一大堆bug是由get或者remove传递进去的参数类型不匹配而造成的。
比如:
Map<Short, String> m = new HashMap();
m.put(new Short((short) 2), "2222");
System.out.println(m.get(2));
上面的代码输出是null。
一般人很难发现传递进去的int和Short类型不匹配,而且IDE,编译器也没有提示。当然通过一些分析工具可以检查出来。
真的感到很困惑,Java中的容器的一些函数,参数都是Object类型,如HashMap中的get,remove函数,Set中的contains函数。
为什么不明确它们的类型?这样编译器可以检查出类型不匹配的错误。
Google之,google的一个工程师给出了答案:http://smallwig.blogspot.com/2007/12/why-does-setcontains-take-object-not-e.html
为了简单起见,以Set容器为例:
定义一个简单的S,只有一个简单的contains函数:
class S<K>{
public void contains(K k){
System.out.println("S<K>,contains(K k)");
}
}
假如我们有个函数,想要处理Foo类的集合:
public void doSomeReading(S<Foo> foos) {
}
要是我们想能同时处理Foo类的子类(如SubFoo)的集合,那应该这样定义:
public void doSomeReading(S<? extends Foo> foos) {
}
一切看起来很好,但是如果我们把代码都合起来,就会发现悲剧了:
class S<K>{
public void contains(K k){
System.out.println("S<K>,contains(K k)");
}
}
class Foo{
}
class SubFoo extends Foo{
}
public void doSomeReading(S<? extends Foo> foos) {
Foo f = new Foo();
SubFoo subFoo = new SubFoo();
foos.contains(f); //这里eclipse会提示出错,这里只有填null时才不会报错
foos.contains(subFoo); //同样错误
foos.contains(null);
}
这时编译器不干了,它表示不能工作了。
原来在S<K>类的定义中,我们明确contains(K k)函数只能接受一个明确类型的参数。
但是在doSomeReading函数中,编译器无法确定到底是什么类型,它是Foo类型,还是SubFoo类型,还是SubSubFoo类型?
编译器无从得知,所以它只允许null类型的参数。
===========================================================
对于这个解析,话说还是有点郁闷。
也有另外的解析,认为和equals函数有关系。不过感觉不大靠谱,这个只能说是一些另类的应用。