在逛 Stack Overflow 的时候,发现了一些访问量像熊耳山一样高的问题,比如说这个:为什么不应该使用Java的原始类型?访问量足足有 205K+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,本文之前的我就是其中之一。
来回顾一下提问者的问题吧:
Java 的原始类型是什么?为什么不要使用原始类型?如果不能使用原始类型,有什么更好的选择呢?
如果大家也被这个问题困扰过,或者正在被困扰,就请随我来,咱们肩并肩手拉手一起梳理一下这个问题,并找出最佳答案。Duang、Duang、Duang,打怪进阶喽!
要理解 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> |
大家可能会有一个疑惑,原始类型用起来很爽啊!因为不用关心放入 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);
}
}
可假如代码写成这样,可真真算得上是糟糕的代码了。
通常来说,为了代码的安全性起见,我们希望代码的错误发生得越早越好,能在编译时就不要在运行时。可使用原始类型的时候,我们发现错误一直到运行时才可能会被检出。
还记得《扁鹊见蔡桓公》的故事吗?
扁鹊见蔡桓公,立有间。扁鹊曰:“君有疾在腠理,不治将恐深。”桓侯曰:“寡人无疾。”扁鹊出,桓侯曰:“医之好治不病以为功。”…居十日,扁鹊望桓侯而还走。桓侯故使人问之,扁鹊曰:“疾在腠理,汤熨之所及也;在肌肤,针石之所及也;在肠胃,火齐之所及也;在骨髓,司命之所属,无奈何也。今在骨髓,臣是以无请也。”居五日,桓侯体痛,使人索扁鹊,已逃秦矣。桓侯遂死。
病情发现得越早,治疗的可能性就越大。同理,代码隐藏的问题发现的越晚,找出根源花费的精力就越大、时间就越多。
如果不能使用原始类型,有什么更好的选择呢?
为了让 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 值的时候就会编译出错。
从另一种层面上来说,这样做削弱了程序的灵活性,但保证了程序的绝对安全性,以及在表达上的明确性。
既然原始类型是不安全的,那为什么 Java 一直允许使用原始类型呢?并且泛型擦除后仍然是个原始类型呢?
答案很简单、很无厘头、很苍白——为了版本兼容!
引入泛型的时候,Java 已经进入到第二个十年(年纪大了),市面上存在大量没有使用 Java 泛型的代码。如果因为版本升级导致它们不能使用,恐怕 Java 也活不到现在,毕竟对用户友好才是一个软件存在的硬道理。
当然了,Java 已经对开发者做出了警示:强烈建议不要在 Java 代码中使用原始类型,未来的版本中可以会禁止使用原始类型,请小心点。
好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了。如果觉得不过瘾,我把本系列文章做了汇总,这里推荐给大家。
五分钟学Java:为什么会发生ArrayIndexOutOfBoundsException?
五分钟学Java:什么是 NullPointerException?
谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。
SADD KEY_NAME VALUE1…VALUEN(将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略;假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合;当集合 key 不是集合类型时,返回一个错误。)127.0.0.1:6379[1]> sadd users lxt zrt(integer) 2127.0.0.1:6379[1]> sad...
一、什么是情感分析情感分析(SA)又称为倾向性分析和意见挖掘,它是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程,其中情感分析还可以细分为情感极性(倾向)分析,情感程度分析,主客观分析等。情感极性分析的目的是对文本进行褒义、贬义、中性的判断。在大多应用场景下,只分为两类。例如对于“喜爱”和“厌恶”这两个词,就属于不同的情感倾向。请噶程度分析主要是对同一情感极性中再进行划分或者细分,以
1.数据挖掘含义数据收集和存储技术的发展使得各组织机构能够积累海量的数据。但是,由于数据量太大,传统的数据分析工具和技术已经不再适用,因此,需要开发新的方法来对数据进行处理。数据挖掘(data mining)就是利用一系列技术和方法从海量数据中找出隐藏于其中的潜在、有用的新知识的过程。在庞大的数据中找到有价值的知识,就好像在一堆沙子中淘金,因此被形象的称为data mining。2.相关概念辨析知识发现与数据挖掘:数据挖掘是知识发现(knowledge discovery)的核心,是其中的一个步
1.下载及安装libsvm3.1下载:里面包含了libsvm和参数寻优的文件,具体参见readme安装:http://www.matlabsky.com/thread-11925-1-1.html2.参数说明:English:libsvm_options:-s svm_type : set type of SVM (default 0) 0 --
AsyncTask是什么 AsyncTask 允许对用户界面执行异步操作。 它会先阻塞工作线程中的操作,然后在 UI 线程中发布结果,而无需您亲自处理线程和/或处理程序。其实AsyncTask就是安卓帮我们对异步处理机制进行了封装的一个类,方便我们进行异步操作。 1、可以使用泛型指定参数类型、进度值和任务最终值 2、方法 doInBackground() 会在工作线程上自动执行
上次整合过一次,遇到这个问题没记下来,现在又遇到了,有必要写一下PS:如果我总结的不对,欢迎各位大佬指正~环境:spring boot 版本 2.1.0 &lt;!--spring boot 版本--&gt; &lt;parent&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;...
实验二:序列组装1.art illumina模拟双末端测序短插入片段和长插入片段库:./art_illumina -ss HS25 -sam -i ./GCF_000146045.2_R64_genomic.fna -p -l 125 -f 10 -m 200 -s 10 -o ./Sc_paired./art_illumina -ss HS25 -sam -i ./GCF_000146045.2_R64_genomic.fna -p -l 125 -f 10 -m 2500 -s 50 -
一个 T 的数组类型的左值如果出现在表达式中会蜕变为一个指向数组第一个成员的指针(除了三种例外情况); 结果指针的类型是 T 的指针。 这就是说, 一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了 &a[0] 一样。三种例外情况:1.数组作为 sizeof 操作符的操作数, 2.数组作为 & 操作符的操作数3.数组作为字符数组
没有什么废话,直接告诉你怎么做这里主要分成两步:1、修改安全组配置,对外开放3306端口2、数据库用户授权-- 用户授权grant all privileges on *.* to '用户名'@'%' identified by '密码' with grant option;-- 刷新权限flush privileges;然后用工具连接数据库就行了...
本文转载于:猿2048网站回到顶部案例 html: <div class='btn-top' id='box'></div> css: <style> .btn-top { wi...
实现从数据库中获取用户的信息,自定义登录逻辑@[email protected]@[email protected]@[email protected]//需要构造出 org.springframework.security.core.userdetails.User 对象并返回/*** String username:用户名* String password: 密码* boolean enabled: 账号是否可用。
36.c/c++程序员面试宝典-表