0. 背景
最近在网上看到了一个问题,觉得挺有意思的,于是就试着分析了一下,并分享给大家。
先看代码:
问题是:对于以上代码中,为何 bb
变量的值会是空的呢?难道不是 byte
自动转型为 int
,再自动装箱为 Integer
吗?
问题来自V2EX : https://v2ex.com/t/657545#reply20
那么,我们接下来试着解答一下这个问题。
1. 问题分析
以上的代码很简单,它做了以下几件事件:
- 初始化了一个 HashMap
类型的变量 map ; - 向map中添加了一对数据<1, "one"="">;1,>
- 初始化一个 byte 类型的变量 b,同时也设置值为1;
- 试图使用变量b作为key从map中取出之前存放的KV对数据<1, "one"="">;1,>
- 结果获取到的为空了;
单从这个顺序描述来看,好像最终的结果确实是不应该为空的,那么问题到底出在哪里呢?
首先呢,我们知道HashMap中存储数据的原理,它内部是通过Key对象的哈希值作为内部存储的索引值的。
同时,HashMap也像其他的Java集合类一样,只接受引用类型的数据的存储,而不接受原始类型。原始类型作为参数传递进去的时候,会被自动装箱成它们分封装类,比如:int –> Integer、byte –> Byte 等等。
所以在以上的示例代码中,作为key的byte类型或者是int 类型必然也是被自动包装成它们的包装类了。所以以上代码其实是相当于:
|
|
这样转换后,问题就明了很多了,在代码中调用put
的时候是以Integer
类型来当作key存储的,而取数据的时候是以 Byte
类型来取的数据,而Java中确定对象是否相等的依据是对象的 equal()
方法。那么Integer(1)
和 Byte(1)
两个连类型都不一样,equals 的结果肯定是false了。
这样分析后,问题就解答了,简而言之,集合类中有自动装箱的存在,所以在涉及到Key的相等判断时,务必要看清除它们的类型。
2. 该如何避免类似的问题呢?
确定了问题后,我们自然要去问自己,在生产环境中,要如何避免这类似的问题呢?而且最好是在编译时就能避免,而不是把问题在运行时才显现出来。
我们来看下HashMap的存取操作相关的方法定义:
从存取方法的定义能够看出来,put 方法有泛型的定义来约束,而get方法只有返回值有泛型约束,而输入参数是没有泛型来约束的,而是接收一个Object
对象。那么,要是HashMap 的get方法有泛型约束就完美了。
正好!Kotlin
中的HashMap就添加了这个功能,在Kotlin中给常用的集合类都进行了扩展,其中就包括HashMap的get方法的泛型约束问题。
那么,就把以上的代码,换成Kotlin实现的版本,如下:
编译下,你就会发现过不了编译~~,所以欢迎入坑Kotlin(没错,这是一篇Kotlin的安利文)。