在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,本文之前的就是其中之一。

来回顾一下提问者的问题吧:

Java 的原始类型是什么?为什么不要使用原始类型?如果不能使用原始类型,有什么更好的选择呢?

如果大家也被这个问题困扰过,或者正在被困扰,就请随我来,咱们肩并肩手拉手一起梳理一下这个问题,并找出最佳答案。Duang、Duang、Duang,打怪进阶喽!

01、Java 的原始类型是什么?

要理解 Java 的原始类型是什么,可以先看一下什么是泛型

List<String> list = null;

其中 list 就是一个泛型,我们通常称之为字符串(String)列表(List),也就是说 list 中只能放字符串类型的元素。

如果我们按照下面这种方式声明 list 的话,它就是一个原始类型。

List list = null;

从 list 的声明当中我们可以对比发现,原始类型没有为容器指定明确的元素类型,所以我们可以在容器中放入一个 String,也可以放入一个 Integer,甚至任意的类型,就像下面这样。

public class RawType {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("沉默王二");
        list.add(18);
        list.add(new RawType());
    }
}

注意哦,编译器没有任何提醒!这预示着 Java 这门强类型的语言竟然有点弱类型的影子了。

PS:关于 Java 中的类型术语,大家可以参照下表。

术语 含义 举例
Parameterized type 参数化类型 List<String>
Actual type parameter 实际类型参数 String
Generic type 泛型类型 List<E>
Formal type parameter 形式类型参数 E
Unbounded wildcard type 无限制通配符类型 List<?>
Raw type 原始类型 List
Bounded type parameter 限制类型参数 <E extends Number>
Bounded wildcard type 限制通配符类型 List<? extends Number>

02、为什么不要使用原始类型?

大家可能会有一个疑惑,原始类型用起来很爽啊!因为不用关心放入 List 的元素到底是什么类型,想放什么就可以放什么,不要太爽啊!

可当我们想要从 List 中把元素取出来使用的时候,可就遇到大麻烦了。

List list = new ArrayList();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

for (Object o : list ) {
    String s = (String) o;
    System.out.println(s);
}

上面这段代码编译的时候没有任何问题,但输出的时候就会抛出 ClassCastException

沉默王二
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at com.cmower.java_demo.programcreek.RawType.main(RawType.java:14)

除非我们使用 instanceof 关键字进行类型判断,就像下面这样。

List list = new ArrayList();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

for (Object o : list ) {
    if (o instanceof String) {
        String s = (String) o;
        System.out.println(s);
    } else if (o instanceof Integer) {
        Integer i = (Integer) o;
        System.out.println(i);
    } else if (o instanceof RawType) {
        RawType raw = (RawType) o;
        System.out.println(raw);
    }
}

可假如代码写成这样,可真真算得上是糟糕的代码了。

通常来说,为了代码的安全性起见,我们希望代码的错误发生得越早越好,能在编译时就不要在运行时。可使用原始类型的时候,我们发现错误一直到运行时才可能会被检出。

还记得《扁鹊见蔡桓公》的故事吗?

扁鹊见蔡桓公,立有间。扁鹊曰:“君有疾在腠理,不治将恐深。”桓侯曰:“寡人无疾。”扁鹊出,桓侯曰:“医之好治不病以为功。”……居十日,扁鹊望桓侯而还走。桓侯故使人问之,扁鹊曰:“疾在腠理,汤熨之所及也;在肌肤,针石之所及也;在肠胃,火齐之所及也;在骨髓,司命之所属,无奈何也。今在骨髓,臣是以无请也。”居五日,桓侯体痛,使人索扁鹊,已逃秦矣。桓侯遂死。

病情发现得越早,治疗的可能性就越大。同理,代码隐藏的问题发现的越晚,找出根源花费的精力就越大、时间就越多。

03、有什么更好的选择呢?

如果不能使用原始类型,有什么更好的选择呢?

为了让 List 能够容纳任意类型的元素,我们可以使用 List<Object>,尽管这并不是一个最优的选择。

List<Object> list = new ArrayList<>();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

鹅鹅鹅,这样的参数化类型 List<Object> 和原始类型 List 之间有区别吗?

当然有了!

List<Object> 至少明确地告诉编译器,该容器可以存放任意类型的对象,没有丢失类型的安全性。

可能我这样的解释会遭到某些抨击:“这不五十步笑百步吗?呵呵。”但我要想表达的是登月男神阿姆斯特朗的那句话:“这是我个人的一小步,却是人类的一大步。”能向前迈一步是一步啊。

那最优的选择是什么呢?

从一开始就为 List 声明具体的类型,比如说 List<String> list,当我们尝试放入一个 int 值的时候就会编译出错。


从另一种层面上来说,这样做削弱了程序的灵活性,但保证了程序的绝对安全性,以及在表达上的明确性。

04、为什么 Java 允许使用原始类型?

既然原始类型是不安全的,那为什么 Java 一直允许使用原始类型呢?并且泛型擦除后仍然是个原始类型呢?

答案很简单、很无厘头、很苍白——为了版本兼容!

引入泛型的时候,Java 已经进入到第二个十年(年纪大了),市面上存在大量没有使用 Java 泛型的代码。如果因为版本升级导致它们不能使用,恐怕 Java 也活不到现在,毕竟对用户友好才是一个软件存在的硬道理。

当然了,Java 已经对开发者做出了警示:强烈建议不要在 Java 代码中使用原始类型,未来的版本中可以会禁止使用原始类型,请小心点。

05、鸣谢

好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,二哥必须要动动手为你点个赞

205K+程序员关注过的问题:为什么不应该使用Java的原始类型?的更多相关文章

  1. 188W+程序员关注过的问题:Java到底是值传递还是引用传递?

    在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个 ...

  2. 【Java】Java程序员面试宝典(第三版)第5章----Java程序设计基本概念

    1.static静态变量,在次级作用域也可以被修改. 2.k++ + k++.第一个自加实际上只有在与计算+k++时补增.详情P36的题目. 3.Java数据类型从低到高分为(byte short c ...

  3. 牛客网程序员面试金典:1.2——原串翻转(java实现)

    问题描述: 请实现一个算法,在不使用额外数据结构和储存空间的情况下,翻转一个给定的字符串(可以使用单个过程变量). 给定一个string iniString,请返回一个string,为翻转后的字符串. ...

  4. 多少牛逼的程序员毁在low逼的英文发音上(JAVA)

    最最常用的关键词及音标 数据类型:boolean.byte.short.int.long.double.char.float.double. 包引入和包声明:import.package. 用于类和接 ...

  5. 对java程序员来说时间格式永远让人挠头来看Java Date Time 教程-时间测量

    在Java中,用System.currentTimeMillis()来测量时间最方便. 你要做的是在某些操作之前获取到时间,然后在这些操作之后你想要测量时间,算出时间差.下面是一个例子: long s ...

  6. 从小工到专家 ——读《Java程序员职场全攻略》有感

    从小工到专家 ——读<Java程序员职场全攻略>有感   <Java程序员职场全攻略>是以故事的形式,向读者介绍Java程序员的职场经验.作者牛开复在北京从事软件开发,已经是一 ...

  7. TCP/IP之四书五经[转自2003.12程序员]

    TCP/IP协议是当前广域网和局域网通用的网络协议,因此,基于TCP/IP的编程就格外重要.从应用上来说,现在直接利用C层次Socket API进行TCP/IP编程的人确实越来越少了,各种现成的框架( ...

  8. 程序员带你学习安卓开发,十天快速入-对比C#学习java语法

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  9. Java程序员面试题集(1-50)(转)

    转:http://blog.csdn.net/jackfrued/article/details/17339393 下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和 ...

随机推荐

  1. LeetCode51 N皇后——经典dfs+回溯(三段式解法)

    代码如下: class Solution { public: // record[row] 该行对应的列 vector<vector<string> > ans; // 结果集 ...

  2. 反汇编分析NSString,你印象中的NSString是这样吗

    我们先来定义三个NSString -(void) testNSString { NSString* a = @"abc"; NSString* b = [NSString stri ...

  3. 扛把子组20191121-10 Scrum立会报告+燃尽图 06

    此作业的要求参见http://edu.cnblogs.com/campus/nenu/2019fall/homework/10070 一.小组情况: 队名:扛把子 组长:孙晓宇 组员:刘信鹏 韩昊 宋 ...

  4. 工作常用4种Java线程锁的特点,性能比较、使用场景

    多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升.虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求. 使用多线程的理由之一是和进程相比,它是一种非常花 ...

  5. 【故障公告】数据库服务器 CPU 近 100% 引发的故障

    抱歉,今天上午 10:48 ~ 10:33 期间,我们所使用的数据库服务(阿里云 RDS 实例 SQL Server 2016 标准版)又出现了 CPU 近 100% 问题,由此给您带来麻烦,请您谅解 ...

  6. opencv resize图片为正方形尺寸

    在深度学习中,模型的输入size通常是正方形尺寸的,比如300 x 300这样.直接resize的话,会把图像拉的变形.通常我们希望resize以后仍然保持图片的宽高比. 例如: 如果直接resize ...

  7. /etc/security/limits.conf配置文件详解

    这个文件主要是用来限制用户对系统资源的使用.是/lib64/security/pam_limits.so模块对应的/etc/serurity/pam_limits的配置文件. # /etc/secur ...

  8. 【前端】 在前端利用数学函数知识+box-shadow解波浪图形

    序 今天正在刷数学函数相关题目,刷到了下面这篇文章,哇哦-有意思. 利用cos和sin实现复杂的曲线.传送门在下面. CSS 技巧一则 -- 在 CSS 中使用三角函数绘制曲线图形及展示动画 正巧在复 ...

  9. docker配置mysql主从与django实现读写分离

    一.搭建主从mysql环境 1 下载mysql镜像 docker pull mysql:5.7 2 运行刚下载的mysql镜像文件 # 运行该命令之前可以使用`docker images`是否下载成功 ...

  10. Select下拉框onchange事件获取option的value值

    首先方法中使用到了jqury,首先导入jqury的插件: 如果jqury的小插件没有的话,就去网上下载一个吧,jqury比原生的js使用起来方便了很多,这里也提供一个我的jqury的下载地址: jqu ...