本故事纯属虚构,如有雷同,纯属巧合。
故事背景
一大早,产品经理小 P 就兴冲冲地跑到小白的座位旁边。
小 P:” 小白兄弟,首先恭喜你获得这个月的公司月度之星呀!我这边有个需求,需要你帮忙支援一下,我已经跟你们领导小 L 打好招呼了,他也同意你暂时调到新项目中支持,这个项目已经和外部谈得差不多了,项目完成后有项目奖金和 outing 团建哦。“
小白:” 既然领导都同意了,我肯定支持,很高兴一起合作!“
小 P:” 现在对方根据我们提供的文档,提出了一个要求,需要我们这边的接口修改后,就可以和对方联调,然后上线了,只差最后一脚了,你一定要帮忙。“
小白:” 能否将需求给我详细解释一下?“
小 P:” 是这样的,之前我们有个接口,是返回用户的各项评分,这个接口已经上线半年了,但客户要求我们不但要返回用户的各项评分,还要将描述一下分数的意义。“
小 P:” 原来接口是这样的 List<Integer> getScoresByUser(UserInfo uinfo)
,返回 60、1500、90 这样的得分,现在该接口要返回 60,活跃度中等【0~100】、1500、粉丝数、90,好评度【0-100】“。
小白:” 不是可以新加一个接口给对方吗?“
小 P:” 不可以,双方已经达成统一了,大老板也拍板了,没法修改,今天开发,明天联调上线。“
小白:“天雷滚滚,伤我心!”
图片来自于网络,版权为原作者所有
旁白:
产品经理口头禅:
这个需求很简单,怎么实现我不管。
哪种语言你随便,不服可以找老板。
横批:明天上线。
寻找解决方案
沟通完成后,小白拖着郁闷的身心,拉下新项目的代码,想着解决方案。先整理思路:
原先的接口原型:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
//todo ,get hot socore
list.add(60);
//todo get fans
list.add(1500);
//todo get evaluation
list.add(90);
System.out.println(Arrays.toString(list.toArray()));
}
打印出结果:
[60, 1500, 90]
新接口想要打印出:
[60,活跃度中等【0~100】,1500,粉丝数,90,好评度【0-100】]
小白一直想吼出,老子不干了!
图片来自于网络,版权为原作者所有
但想想现在还是菜鸟,忍忍忍。
小白想到,“对了,我解决不了,可以找扫地僧问问。他经验丰富,碰到过各种变态需求,应该有思路吧。”
考虑到下个月要不要流落街头的问题,小 B 赶紧找到扫地僧,说了产品经理小 P 的要求,问他有没有办法解决。
扫地僧:” 这个需求有点麻烦,一周的下午茶,我帮你解决,怎么样?“
小白:” 有点狠,但没有问题,知识无价嘛。“
看小白那么爽快,扫地僧放下手头的《代码大全 2》,扫了一眼代码,敲了一通键盘后,又拿起了《代码大全 2》看了起来。
扫地僧:“好了,你运行一下吧!”。
小白赶紧试了一下,果然可以了!
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
try {
//todo ,get hot socore
list.add(60);
list.getClass().getMethod("add", Object.class).invoke(list, "活跃度中等【0~100】");
//todo get fans
list.add(1500);
list.getClass().getMethod("add", Object.class).invoke(list, "粉丝数,排名 3689 位");
//todo get evaluation
list.add(90);
list.getClass().getMethod("add", Object.class).invoke(list, "用户评价,超越 92%的用户");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Arrays.toString(list.toArray()));
}
怎么回事?
图片来自于网络,版权为原作者所有
小白暗想,一周的下午茶总得知道原因吧?不能白白浪费这个机会,于是继续缠着扫地僧问了起来,扫地僧被烦得无奈,遂慢慢给他讲解。原来因为种种原因,Java 不能实现真正的泛型,只能使用类型擦除来实现伪泛型。
类型擦除
当编译器对带有泛型的 Java 代码进行编译时,它会去执行类型检查和类型推断,然后生成普通的不带泛型的字节码,这种字节码可以被一般的 Java 虚拟机接收并执行,这种技术被称为擦除(erasure)。这样虽然不会有类型膨胀问题,但是也引起来许多新问题,所以,SUN 对这些问题做出了种种限制,避免我们发生各种错误。
编译器可以在对源程序(带有泛型的 Java 代码)进行编译时使用泛型类型信息保证类型安全,对大量如果没有泛型就不会去验证的类型安全约束进行验证,同时在生成的字节码当中,将这些类型信息清除掉。
追根究底
小白:“那您老是怎么知道的?类型擦除是在哪里规定的呢?”
扫地僧:“问到点子上了,JLS4.6 Type Erasure 有详细的介绍规定,你自己去看吧,不要烦我!”
参考资料:
【1】Java 解惑