Java 中假泛型导致的一个坑
Contents
情景
公司里一位程序员,写了段代码:
@Override
public Set<Integer> getUserId() {
Set<Integer> userIdSet;
Object value = LocalCache.getValue(CacheConstants.USERID_KEY);
if (value != null) {
userIdSet = (Set<Integer>) value;
} else {
userIdSet = redisTemplate.opsForSet().members(CacheConstants.USERID_KEY);
LocalCache.putValue(CacheConstants.USERID_KEY, userIdSet, 300);
}
return userIdSet;
}
然后判断
Set<Integer> userIdSet = taskService.getUserId();
if (task != null && userIdSet.contains(task.getUserId())) {
//do somehting
} else {
Loggers.RUNNING_LOG.info("unmatch, {}, {}", userIdSet.toString(),
task.getUserId());
}
发现它输出了的日志:
unmatch, [2, 38, 39, 40, 41, 42, 43, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 1000], 60
看这样子的数据, Set 里的集合,是有 60 这个值的。那为什么它是 unmatch 呢?这就很奇怪。
原因
redisTemplate.opsForSet().members()
该方法返回的是泛型数据,它的接口声明为:
Set<V> members(K key);
而且由于 redisTemplate 并没有添加泛型信息(项目中的 redis 操作,全是 string 来进行编码和解码的):
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
所以,导致
userIdSet = redisTemplate.opsForSet().members(CacheConstants.USERID_KEY);
这里的赋值,直接通过了编译(因为目前Java对泛型的支持,仅仅是通过编译器级别来实现的,而在运行时 runtime 中并没有真正支持。
所以,在实际上, userIdSet 里的是 String 类型,而不是 Integer 类型。
这样子的结果就是使用 String 的 Set 去调用 contains(Integer) ,肯定就会导致 false 啦,然后就出现了上面的日志的打印。
解决
知道原因,那就可以进行相应的完善啦
RedisTemplate 注入里,要加上泛型信息,即修改为
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate<String, String> redisTemplate;
然后就可以看到相关的地方会报相应的编译时错误了,然后就可以按错误信息的提示来进行修改了。
教训: 在一个有泛型的类或方法中,一定要添加泛型的信息!!以免出Bug或掉进坑。