今天遇到一个小问题,让我感觉Java的泛型(因为背负了历史的包袱导致的)有点鸡肋啊。

我们经常会遇到要一些自定义的key-value字符串,比如:

"key1:1k;key2:2;key3:3"

通常编码的时候会将它转换为一个Map这样方便操作,因为key和value的类型不一定(可能是int也可能是String等),于是我用Java写了一个简单的泛型方法:

@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> getMap(String source, String firstSplit, String secondSplit) { Map<K, V> result = new HashMap<K, V>();
if (source.equals("")) {
return result;
}
String[] strings = source.split(firstSplit);
for (int i = 0; i < strings.length; i++) {
String[] tmp = strings[i].split(secondSplit);
if (tmp.length == 2) {
result.put((K) tmp[0], (V) tmp[1]);
// System.out.println("(K) tmp[0]:"+((K) tmp[0]).getClass());
// System.out.println("(V) tmp[1]:"+((V) tmp[1]).getClass());
}
} return result;
}

看上去貌似可以正常工作的,用上面的字符串举例子,我应该希望得到的是Map<String, Integer>这样一个结果。

String test = "key1:1k;key2:2;key:3";
Map<String, Integer> map = getMap(test, ";", ":"); for (Entry<String, Integer> entry : map.entrySet()) {
Integer value = entry.getValue();
}

上面的代码编译时完全没问题的,但是一运行:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at ossp.demo.generic.GenericDemo.main(GenericDemo.java:38)

报的是类型转换错误,String不能转换为Interger类型?但是明明entry.getValue()结果就是Interger类型啊,难道不是吗?

一开始很疑惑,但稍微一想就明白了,哦!Java泛型用的是“擦除”法,完全是编译期的,运行时已经没有泛型参数的类型信息了,也就是说运行时所有的泛型参数都被替换成了Object(如果有泛型约束(.net是这么叫的)是不是就替换成上限类型?)所以上面的泛型方法其实就等价于:

public static Map<Object, Object> getMap(String source, String firstSplit, String secondSplit) {

        Map<Object, Object> result = new HashMap<Object, Object>();
if (source.equals("")) {
return result;
}
String[] strings = source.split(firstSplit);
for (int i = 0; i < strings.length; i++) {
String[] tmp = strings[i].split(secondSplit);
if (tmp.length == 2) {
result.put((Object) tmp[0], (Object) tmp[1]);
}
} return result;
}

也就是说entry.getValue();返回的其实是一个Object对象的,它的类型应该是java.lang.String,于是我想那么这样转换一下应该可以了:

Integer value = Integer.valueOf(entry.getValue());

但是让我郁闷的是仍然报之前的错误,鼠标点上去,智能提示看执行的应该是参数为int的重载反复,额,又绕回去了。

那怎么才能得到我想得到那个Integer的value呢???最后我发现这样是可以的:

Integer value = Integer.valueOf(String.valueOf(entry.getValue()));

我靠!这太让我无语了。不光如此我发现直接执行下面这行代码也会报类型转换错误:

System.out.println(entry.getValue().getClass());

既然entry.getValue()的类型是java.lang.String,为什么Map<String, Integer> map = getMap2(test, ";", ":");和Entry<Object, Integer> entry : map.entrySet()这两行又都能编译通过呢?想想还是万恶的“类型擦除“的原因,我们先看看C#里的情况。

 Dictionary<string, int> dic1 = new Dictionary<string, int>();
Dictionary<string, double> dic2 = new Dictionary<string, double>();
Console.WriteLine(dic1);
Console.WriteLine(dic2);

我们知道.NET泛型将每个类型参数理解为一个独立的类型,所以上面dic1和dic2的类型是不一样的:

但是在Java里因为“类型擦除“实际上Map<String,Interger>和Map<String,Double>的类型都是:java.util.HashMap

Map<String, Integer> map1 = new HashMap<String, Integer>();
Map<String, Double> map2 = new HashMap<String, Double>();
System.out.println(map1.getClass());
System.out.println(map2.getClass());

这样看来上面的代码编译通过是必须的,那么这智能提示有什么意义呢(编译期的YY?)。

我们看看同样的问题C#是怎么解决的。一开始我以为像Java那样直接强制类型转换就可以:

或者这样:

这些都是不行的。但是只要运行运行时还有类型参数的信息,那么肯定是有办法办到的,.NET中库中就有现成的这样一个方法:Convert.ChangeType,于是我们可以写出下面这个辅助泛型方法:

    static V GenericCast<U, V>(U obj)
{
return (V)Convert.ChangeType(obj, typeof(V));
}

于是乎为了解决我的问题,我可以写这样一个泛型方法了:

   static Dictionary<K, V> ToMap<K, V>(string source, string firstSplit, string secondSpilt)
{
Dictionary<K, V> result = new Dictionary<K, V>(); if (String.IsNullOrEmpty(source))
{
return result;
} string[] info1 = source.Split(new string[] { firstSplit }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in info1)
{
string[] info2 = item.Split(new string[] { secondSpilt }, StringSplitOptions.RemoveEmptyEntries);
if (info2.Length == )
{
result.Add(GenericCast<string, K>(info2[]), GenericCast<string, V>(info2[]));
}
} return result;
 string test = "key1:1.1;key2:2;key3:3";
Dictionary<string, double> map = ToMap<string, double>(test, ";", ":"); foreach (var item in map)
{
Console.WriteLine(item.Key + ":" + item.Value);
}

并不是想黑Java,只是之前用C#的泛型用的比较爽,用Java的总感觉有点食之无味,弃之可惜。

遇到个小问题,Java泛型真的是鸡肋吗?的更多相关文章

  1. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  2. 一个小栗子聊聊JAVA泛型基础

    背景 周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点: 日志信息严重丢失,茫茫代码毫无头绪. 对泛型的认识不够,导致代码出现了BUG. 第一个原因可以通过以后编码 ...

  3. 类型擦除真的能完全擦除一切信息吗?java 泛型揭秘

    背景 我们都知道泛型本质上是提供类型的"类型参数",它们也被称为参数化类型(parameterized type)或参量多态(parametric polymorphism).其实 ...

  4. Java泛型总结

    1. 什么是泛型?泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的 ...

  5. java泛型的讲解

    java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指 ...

  6. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  7. Java基础学习总结(83)——Java泛型总结

    1. 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型 ...

  8. Java:泛型的理解

    本文源自参考<Think in Java>,多篇博文以及阅读源码的总结 前言 Java中的泛型每各人都在使用,但是它底层的实现方法是什么呢,为何要这样实现,这样实现的优缺点有哪些,怎么解决 ...

  9. 用了这么多年的 Java 泛型,你对它到底有多了解?

    作为一个 Java 程序员,日常编程早就离不开泛型.泛型自从 JDK1.5 引进之后,真的非常提高生产力.一个简单的泛型 T,寥寥几行代码, 就可以让我们在使用过程中动态替换成任何想要的类型,再也不用 ...

随机推荐

  1. TPLINK GPL code 简要分析

    从TPLINK官网下载了GPL code,下载后文件名是wr841nv9_en_gpl.tar.gz, 但是无论是linux还是windows下解压都提示压缩包有问题,不过还是可以解压出完整的目录的. ...

  2. StretchDIBits函数

    该函数将DIB中矩形区域内像素使用的颜色数据拷贝到指定的目标矩形中.如果目标矩形比源矩形大小要大,那么函数对颜色数据的行和列进行拉伸,以与目标矩形匹配.如果目标矩形大小要比源矩形小,那么该函数通过使用 ...

  3. 深入探究javascript的 {} 语句块

    今日学习解析json字符串,用到了一个eval()方法,解析字符串的时候为什么需要加上括号呢?摸不着头脑.原来javascript中{}语句块具有二义性,不加括号会出错,理解这种二义性对我们理解jav ...

  4. js对象的两种写法

    <script>     //定义一个对象,提供对应的方法或者属性     var s = {         sd1: function () { },         sd2: fun ...

  5. unity3d 纹理动画

    不知道大家有没有玩过赛车游戏 赛车游戏的跑道有路标,如下图 玩过赛车游戏的都知道,大多数赛车游戏的路标是会动的,如上图,它会从右往左运动 不会发动态图,大家脑补一下吧 没有玩过赛车游戏的也不要紧,大家 ...

  6. sql server 2008 操作数据表

    SQL Server表   表的类型:   ①临时表 临时表可用来处理中间数据或者用临时表 与其它连接共享进行中的工作.临时表只 能放在tempdb中. 私有临时表(#) 全局临时表(##)   ②系 ...

  7. Javascript基础系列之(七)函数(定义和调运函数)

    函数是一个可以随时运行的语句,简单说,函数是完成某个功能的一组语句,它接受0或者多个参数. 函数的基本语法如下 function functionName([arg0,arg1,......argN] ...

  8. Moqui学习之代码分析mantle priceServices.xml

    <?xml version="1.0" encoding="UTF-8"?> <!-- This software is in the pub ...

  9. 使用background和background-image对CSS优先级造成影响

    在写一个关于背景图的CSS时候发现一个奇怪的现象, 原图: 如下代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitiona ...

  10. dell r710 服务器配置RAID5(3块硬盘做RAID5,另外再弄一块做数据冗余盘)

    本文完全转载于:http://www.jb51.net/article/53814.htm,只为做笔记使用 ①4块硬盘做成RAID5 ②3块硬盘做RAID5,一块硬盘做热备盘 这两种配置之间的区别.大 ...