这其实是我遇到的一个线上bug,在这里分享给大家。

如果是用反射,那就很简单了,毕竟泛型只是在编译期进行约束,对运行期是无能为力的。

想想看,如果不使用反射,有没有办法做到呢?

问题起因

在我们公司的实际业务中,有一段类似于这样逻辑的代码,文章最后会放出做测试构造的getList()方法:

    /**
* 主要业务逻辑
*/
public static void main(String[] args) {
// 从数据库查询数据列表,不用关注里面的实现细节
List<DataBO> list = getList(); // 获取所有“a”字段的值的集合
List<Integer> integerList = toList(list, "a"); if (integerList.contains(1)) {
System.out.println("集合里包含1,处理对应的逻辑");
} else {
System.out.println("集合里不包含1,处理对应的逻辑");
}
} /**
* 这是公司提供的一个公共工具方法,获取集合中,每个对象的某个字段的值的集合
*
* @param list 数据对象集合
* @param key 字段
* @return 值的集合
*/
public static <T> List<T> toList(List<DataBO> list, String key) {
return list.stream()
.filter(x -> x.get(key) != null)
.map(x -> (T)x.get(key))
.collect(Collectors.toList());
}

其中的DataBO对象简化如下:

public class DataBO {

    /** 数据库的一条数据,key是列,value是值 */
private Map<String, Object> map = new HashMap<>(); public Object get(String key) {
return map.get(key);
} public void set(String key, Object value) {
map.put(key, value);
} @Override
public String toString() {
return "DataBO{" + "map=" + map + '}';
} }

原本我这里的业务需求是,取列表数据中,所有“a”字段的值出来,判断其中是否含有1。

已知数据库里“a”字段定义为int类型,并且确认了有一条数据在“a”字段上存的是1。但是代码上线一跑,出bug了。

查出来怎么就走到“不包含1”的分支里去了呢?也没有报错,难道底层服务的getList()方法有什么特殊处理,把数据库a=1的那条数据给过滤掉了吗?

问题定位

于是我加了点日志,把listintergerList的元素打印出来,看看里面到底存了什么东西。于是又上线一版,观察一看,神奇的事情出现了,里面明明有1啊??!为啥会走到下面“不包含1”的分支呢?见鬼了!

于是我只能本地debug了一下,才发现数据库查到的集合里,“a”字段返回的是个字符串"1"!而ArrayList的contains()方法,底层是用equals()去比较是否存在的。"1".equals(1),结果肯定是false,所以认为不存在。

好吧,虽然数据库的“a”字段定义为int类型,但是底层服务估计哪里有bug,把Integer类型的字段,转换成了String类型返回给上层服务了。

但转念一向,不对啊,我明明定义的是List<Integer>类型的变量,如果是这样的话,就算查出来"a"字段不是个Integer类型的值,那toList()方法也应该是抛个java.lang.ClassCastException才对,怎么可能正常往下走呢?List<Integer>变量指向的对象里,为什么会存进去一个字符串呢?为什么toList()方法的.map(x -> (T)x.get(key))这一行没有报错呢?

问题解析

问题很明显就是出在了toList()方法里,那个强制类型转换并没有生效。开头我们说了,java的泛型,只是在编译期进行约束,对运行期是无能为力的。那么我们首先就应该想到的就是java的泛型擦除机制,我们对demo类进行编译、再反编译看看。

反编译可以发现,原来toList()方法中,强制类型转换被擦除了。所以返回的其实并不是List<Integer>对象,而是List对象,没有泛型限制。很明显是这个方法有bug,其实就是泛型方法使用错误了。

toList()是公司内部提供的公共工具类方法,那么我们如果还是要做到原来这个方法的效果,该怎么修改这个方法呢?

问题修复

本来这个线上bug到这里就已经搞清楚了,如果只是要快速修复上线也很容易就能解决,把toList()方法返回的集合改成List,然后判断集合是否包含字符串"1"就行。

但我们想,如果后面又有别的同事遇到这个问题了怎么办呢,也会一脸懵逼,最好还是希望toList()方法抛出个java.lang.ClassCastException。所以我们这么修改下toList()方法,增加一个参数,告诉方法你希望返回一个什么类型的值:

这样的话,如果toList()方法还是返回原来的List<Integer>,就会抛异常:

而且如果前后限制的类型不一致,编译期也会报错,泛型就起作用了:

到此这个问题彻底解决。

本文用于测试构造的getList()方法:

    /**
* 查数据库,获取数据对象的集合
*
* @return 数据对象的集合
*/
public static List<DataBO> getList() {
// 这个list是从数据库查出来的
List<DataBO> list = new ArrayList<>();
DataBO db1 = new DataBO();
db1.set("a", "1");
DataBO db2 = new DataBO();
db2.set("a", 2);
list.add(db1);
list.add(db2);
return list;
}

List<Integer>里有可能存String类型元素吗?的更多相关文章

  1. C++里的int 和string类型相互转换

    C++不像Java和C#一样在进行数据类型转换时直接调用一些类方法就可以了,使用起来很简单. 一个很简单的例子就是string str=“D:\\”+1+“.txt”;这在Java或者C#里面是可以自 ...

  2. Java 类类型之 String 类型

    类类型 引用数据类型存的都是地址,通过地址指向对象: 基本数据类型存的都是具体值: 字符串 (String) 类型 特点: 1.字符创都是对象: 2.一旦初始化,不能被更改,字符串缓冲区支持可变的字符 ...

  3. String类型函数传递问题

    String类型函数传递问题 问题 以前没有注意过的一个问题, 最近在使用String类型作为函数入参的时候, 发现函数内对于String类型的改变并不会影响到外层调用对象本身; 结论 (先说结论) ...

  4. 通过反射 往泛型Integer的集合里添加String 类型的数据 Day25

    package com.sxt.method1; import java.lang.reflect.Method; /* * 需求:通过反射 往泛型Integer的集合里添加String 类型的数据 ...

  5. ArrayList list = new ArrayList()在这个泛型为Integer的ArrayList中存放一个String类型的对象

    java面试要点---ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象. ArrayList list ...

  6. 如果不空null并且不是空字符串才去修改这个值,但这样写只能针对字符串(String)类型,如果是Integer类型的话就会有问题了。 int i = 0; i!=''。 mybatis中会返回tr

    mybatis 参数为Integer型数据并赋值0时,有这样一个问题: mybatis.xml中有if判断条件判断参数不为空时,赋值为0的Integer参数被mybatis判断为空,因此不执行< ...

  7. Mysql数据库里面的String类型依照数字来排序以及按时间排序的sql语句

    今天做项目的时候,遇到个小小的问题,在数据库中查询的时候,要用String类型的ID进行一下排序!(注:ID字段为 varchar 类型) 解决的方法: 如: SELECT * FROM  Stude ...

  8. JAVA String类型和原型模式

    如上例所述,变量a,b和它们的值10,20都是存在栈里面,声明的所以String类型的引用也都是存在栈里.而字符串abc是存在字符串常量池中,new出来的String对象则是存在堆里. String ...

  9. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

随机推荐

  1. 【LeetCode】829. Consecutive Numbers Sum 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 数学方法 日期 题目地址:https://leetc ...

  2. TKE用户故事 | 作业帮检索服务基于Fluid的计算存储分离实践

    作者 吕亚霖,2019年加入作业帮,作业帮基础架构-架构研发团队负责人,在作业帮期间主导了云原生架构演进.推动实施容器化改造.服务治理.GO微服务框架.DevOps的落地实践. 张浩然,2019年加入 ...

  3. Scale-Invariant Error

    目录 概 主要内容 代码 Eigen D., Puhrsch C. and Fergus R. Depth Map Prediction from a Single Image using a Mul ...

  4. 教你10分钟对接人大金仓EF Core 6.x

    前言 目前.NET Core中据我了解到除了官方的EF Core外,还用的比较多的ORM框架(恕我孤陋寡闻哈,可能还有别的)有FreeSql.SqlSugar(排名不分先后).FreeSql和SqlS ...

  5. 关于使用JupyterNotebook运行代码运行到一半会闪退的问题

    前几个星期使用Jupyter远程使用师兄的电脑跑了一段沐老师的代码学习,但是总是跑了一段就闪退,同时因为是远程桌面,远程桌面也被挤掉了. 具体表现就是:运行代码--表现正常,打印记录--远程桌面掉线- ...

  6. 编写Java程序,在子类老虎中重写父类动物的吃食方法

    返回本章节 返回作业目录 需求说明: 在子类老虎中重写父类动物的吃食方法 实现思路: 在子类老虎中重写父类动物的吃食方法的实现思路如下: 创建各种动物的父类Animal类,在该类中定义eat()方法. ...

  7. Python学习笔记:利用pd.get_dummies实现哑变量编码

    一.理论介绍 虚拟变量(dummy variable)也叫哑变量,是一种将多分类变量转换为二分变量的一种形式. 如果多分类变量有k个类别,则可以转化为k-1个二分变量. 需要有一个参照的类别. 在非线 ...

  8. Eclipse+Maven+JDK+tomcat搭建java的开发环境

    由于最近有几个同事都在学习java方面的东西,所以我写个博文做下笔记,其中遇到过很多个坑,这里就不多说了 首先,我用的是Eclipse+Maven的组合,用Ecplise是周边java开发的同事用这个 ...

  9. spring boot 集群 + Nginx --- 心得

    1.前言 已经掌握了spring cloud 得使用 ,但这是在内部网络做业务 ,现在需要 在外部网络 访问内部网络 服务 ,引入了 服务端负载均衡 Nginx , Nginx 根据预定的策略 ,将请 ...

  10. Centos7 暂时记录

    chown 修改属主和属组信息 chown -R 对目录所有子的子目录和文件进行修改属主信息 w命令 USER TTY FROM    LOGIN@ IDLE JCPU PCPU WHATroot   ...