为什么Java中的HashMap<K, V>的get函数是get(Object key),而不是get(K key)?

简介: 帮别人的代码改bug,发现有一大堆bug是由get或者remove传递进去的参数类型不匹配而造成的。比如: Map m = new HashMap(); m.put(new Short((short) 2), "2222"); System.out.println(m.get(2));上面的代码输出是null。

帮别人的代码改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函数有关系。不过感觉不大靠谱,这个只能说是一些另类的应用。

http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic


相关文章
|
6月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
181 4
|
8月前
|
JavaScript 前端开发 开发者
讲述Vue框架中用于对象响应式变化的Object.defineProperty函数。
综上所述,Vue.js通过 `Object.defineProperty()`提供了强大的响应式能力,使得状态管理变得简洁高效。这种能力是Vue.js受到广大开发者青睐的重要原因之一。尽管Vue 3.x使用Proxy替代了该方法,但对于Vue 2.x及其之前版本,`Object.defineProperty()`是理解Vue.js内部工作机制不可或缺的一部分。
261 27
Java之HashMap详解
本文介绍了Java中HashMap的源码实现(基于JDK 1.8)。HashMap是基于哈希表的Map接口实现,允许空值和空键,不同步且线程不安全。文章详细解析了HashMap的数据结构、主要方法(如初始化、put、get、resize等)的实现,以及树化和反树化的机制。此外,还对比了JDK 7和JDK 8中HashMap的主要差异,并提供了使用HashMap时的一些注意事项。
441 2
Java之HashMap详解
|
存储 安全 Java
Java 集合框架中的老炮与新秀:HashTable 和 HashMap 谁更胜一筹?
嗨,大家好,我是技术伙伴小米。今天通过讲故事的方式,详细介绍 Java 中 HashMap 和 HashTable 的区别。从版本、线程安全、null 值支持、性能及迭代器行为等方面对比,帮助你轻松应对面试中的经典问题。HashMap 更高效灵活,适合单线程或需手动处理线程安全的场景;HashTable 较古老,线程安全但性能不佳。现代项目推荐使用 ConcurrentHashMap。关注我的公众号“软件求生”,获取更多技术干货!
266 3
|
9月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
436 3
|
存储 缓存 安全
Java HashMap详解及实现原理
Java HashMap是Java集合框架中常用的Map接口实现,基于哈希表结构,允许null键和值,提供高效的存取操作。它通过哈希函数将键映射到数组索引,并使用链表或红黑树解决哈希冲突。HashMap非线程安全,多线程环境下需注意并发问题,常用解决方案包括ConcurrentHashMap和Collections.synchronizedMap()。此外,合理设置初始化容量和加载因子、重写hashCode()和equals()方法有助于提高性能和避免哈希冲突。
775 17
Java HashMap详解及实现原理
|
安全 Java
Object取值转java对象
通过本文的介绍,我们了解了几种将 `Object`类型转换为Java对象的方法,包括强制类型转换、使用 `instanceof`检查类型和泛型方法等。此外,还探讨了在集合、反射和序列化等常见场景中的应用。掌握这些方法和技巧,有助于编写更健壮和类型安全的Java代码。
809 17
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
991 5
|
存储 缓存 安全
在Java的Map家族中,HashMap和TreeMap各具特色
【10月更文挑战第19天】在Java的Map家族中,HashMap和TreeMap各具特色。HashMap基于哈希表实现,提供O(1)时间复杂度的高效操作,适合性能要求高的场景;TreeMap基于红黑树,提供O(log n)时间复杂度的有序操作,适合需要排序和范围查询的场景。两者在不同需求下各有优势,选择时需根据具体应用场景权衡。
194 2
|
12月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
381 1