上篇,kotlin如何解決java開發痛點,讓程序員happier 寫的很長,大叔覺得在快餐式學習的時代,沒幾我的會看到最後。沒想到,看完整篇的掘友還很多。html
@懶洋君 鼓勵大叔,讓大叔再寫一篇,多寫幾個痛點,說是會來給大叔點贊,但願不是騙大叔的~ 哈~java
今天這篇比上篇還長,你還會看完整篇嗎?哈哈哈~~python
咱們先來聊點有趣的八卦:谷歌爲什麼選擇kotlin,而不是繼續改造java?android
谷歌選擇kotlin的緣由真的是由於kotlin比java更好用嗎?git
大叔能夠確定的說,「kotlin比java更好用」,這確定不是谷歌選擇kotlin的緣由。程序員
以谷歌的研發實力,徹底有能力,參與到java的發展中來,讓java變的愈來愈好用。甚至比kotlin還要好用。github
2011~2014年java連續三年 拖更,2014年~2017年,又拖更三年。面試
以後恢復了發版節奏【1年2個版本】。隨後的更新確實讓java愈來愈好用了。編程
甚至有些人說,java愈來愈像kotlin了。bash
咱們在上一篇中討論的:
kotlin字符串支持,三引號""" java14也有這個特性了。【java14今年三月份發佈的】
kotlin的use函數,自動關閉closeable java7 也有try-with-resources的寫法,解決相似的問題。
咱們下文會討論的:
when表達式,java14,switch也支持了相似的表達式了。
智能類型轉換 java14的類型轉換也開始愈來愈智能了。
說這麼多,想表達的觀點就是: java徹底能夠愈來愈好用。谷歌徹底能夠參與到java的迭代,讓java愈來愈好用,甚至能夠坐等sun公司借鑑更多語言的特性。沒有必要傷筋動骨,把android首選語言換成kotlin,說實話這麼多開發者,換一門語言的成本得有多高。並非全部開發者都像掘友們還有大叔這樣熱愛學習。
谷歌選擇kotlin的最直接緣由,是由於sun公司碰瓷。
2010開始sun公司 起訴 谷歌:在Android上使用Java代碼侵犯版權和專利權。
歷史是驚人的類似。sun公司曾經成功碰瓷過微軟。哈哈哈。。。
13年後,sun公司,故技重施,你說谷歌願意重蹈微軟的覆轍嗎?
咱們再來,看看android官方網站,關於room的java的教程:
All rights reserved. Java is a registered trademark of Oracle and/or its affiliates.
谷歌的用到java的教程中,明確標出了:版權全部。Java是Oracle公司全部。
谷歌選擇kotlin只是爲了擺脫專利訴訟而已。固然kotlin本身也爭氣。確實表現優異。
再請你們吃一個瓜:
google前CEO,埃裏克·施密特,擔任過sun公司高管,並且是特別高的那種。
1983年,施密特加盟Sun公司,前後擔任首席技術官和首席執行官。
首任工程部副總裁韋恩.羅森(Wayne Rosen),也曾經任職過sun公司。
以上,只是大叔我的觀點,純屬意淫,沒有sun公司和谷歌公司的實錘資料。請你們保持獨立思考。
咱們進入今天的第二個主題。繼續一個個語法特性來說,kotlin如何解決java開發痛點。
沒有看上一篇 kotlin如何解決java開發痛點,讓程序員happier 的朋友們,能夠先看下上一篇,這樣會更有連續性。
咱們先從一道java考題入手。大叔仍是一位亮仔的時候,被這道題折磨的痛並快樂着,哈哈~
大多數 java老鳥 都認識這道題。
public static void intEqual(){
Integer int127First = 127;
Integer int127Second = 127;
System.out.println("int127First == int127Second :" + (int127First == int127Second));
Integer int128First = 128;
Integer int128Second = 128;
System.out.println("int128First == int128Second :" + (int128First == int128Second));
}
複製代碼
大叔的靈魂拷問:日誌會輸出什麼?
大叔的靈魂拷問:日誌會輸出什麼?
大叔的靈魂拷問:日誌會輸出什麼?
眼熟嗎,是否是面試的時候遇到過?
是否是大學考試的時候考過?哈哈~
日誌輸出:
int127First == int127Second :true
int128First == int128Second :false
複製代碼
原理解析:
public static void intEqual(){
Integer int127First = 127;
Integer int127Second = 127;
//由於 -128到127 的整數 都在常量池裏
//因此 int127First 與 int127Second 指向了,常量池中同一個對象,因此這裏輸出true
System.out.println("int127First == int127Second :" + (int127First == int127Second));
Integer int128First = 128;
Integer int128Second = 128;
//由於128不在常量池裏,因此 int128First和int128Second 是不一樣的對象,因此這裏輸出false
System.out.println("int128First == int128Second :" + (int128First == int128Second));
}
複製代碼
咱們再來看看 kotlin的代碼:
fun intEqual() {
val int127First = 127
val int127Second = 127
println("int127First == int127Second :" + (int127First == int127Second))
val int128First = 128
val int128Second = 128
println("int128First == int128Second :" + (int128First == int128Second))
}
複製代碼
大叔的靈魂拷問:日誌會輸出什麼?
大叔的靈魂拷問:日誌會輸出什麼?
大叔的靈魂拷問:日誌會輸出什麼?
日誌輸出:
int127First == int127Second :true
int128First == int128Second :true
複製代碼
對你沒有看錯,兩個結果都是true。
why?
爲何?
結構相等 ==:用 equals() 判斷相等性
引用相等=== :判斷兩個引用是否指向同一對象
大叔自我拷問,結構相等這個操做符有什麼用呢?他能解決什麼痛點呢?
大叔自我拷問,結構相等這個操做符有什麼用呢?他能解決什麼痛點呢?
大叔自我拷問,結構相等這個操做符有什麼用呢?他能解決什麼痛點呢?
jvm中存在常量池機制,字符串、部分包裝類對象有可能存在常量池,也可能不在常量池。
引用相等運算結果,與常量池耦合,若是兩個對象都在常量池中,運算結果爲true,若是有一個不在常量池中,或者都不在常量池中,即便兩個對象內容相同,運算結果也是false。
致使,引用相等運算,有點複雜,一不留神可能就搞錯了。
kotlin的結構相等,跟常量池無關了,直接經過equal方法判斷,運算結果與常量池不耦合,無論對象在不在常量池,運算結果都是同樣的。
不只減小了初學者的學習成本。也減小了研發老鳥的犯錯成本。
三等號「===」 kotlin的三等號運算效果,就是java的雙等號「==」,熟悉python的朋友,應該一點都不陌生。由於python也是這樣的。
而且,而且,而且,kotlin中,結構相等 == 還會幫咱們自動判空。咱們看下面兩段代碼。
首先是java代碼。JavaEqualMain.java
/** * 判斷字符串是否相等 */
public static boolean isTextEqual(String str1, String str2){
if(str1 == str2){
return true;
}
if(str1 != null && str2 != null){//由於str1和str2可能有一個爲null,因此咱們要判空
return str1.equals(str2);
}
return false;
}
複製代碼
咱們再看看看kotlin,怎麼實現:KtEqualMain.kt
/** * 判斷字符串是否相等 */
fun isTextEqual(str1: String?, str2: String?): Boolean {
return str1 == str2
}
複製代碼
是的,你沒有看錯,kotlin就是這麼簡潔。
kotlin的 isTextEqual()方法,會編譯器被翻譯成isTextEqual2():
fun isTextEqual2(str1: String?, str2: String?):Boolean{
return str1?.equals(str2) ?: (str2 === null)
}
複製代碼
上面這段代碼段,是官網文檔的解釋。www.kotlincn.net/docs/refere…
kotlin不熟悉的同窗,可能會比較迷茫。
大叔把他改一下,你就明白了。
fun isTextEqual3(str1: String?, str2: String?):Boolean{
if (str1 == null){
return str2 == null
}
return str1.equals(str2)
}
複製代碼
這就是,會自動判空的緣由。
kotlin 提供了兩個相等操做符 == 和 ===
== 【結構相等】,用 equals() 檢測。
至關於調用 Object.equals() 方法,而且處理了,對象爲null狀況,無需手動判空處理。
=== 【引用相等】,兩個引用是否指向同一對象
至關於java的 ==
這個題也是面試常考的題,咱們一塊兒觸類旁通一下哈。
歡迎,老鐵們,在評論區說出你的答案:
java代碼:JavaEqualMain.java
public static void stringEqual(){
String strBuilder1 = new StringBuilder("IT互聯網大叔").toString();
String strBuilder2 = new StringBuilder("IT互聯網大叔").toString();
System.out.println("strBuilder1 == strBuilder2 :" + (strBuilder1 == strBuilder2));
String str1 = "IT互聯網大叔";
String str2 = "IT互聯網大叔";
System.out.println("str1 == str2 :" + (str1 == str2));
}
複製代碼
大叔的靈魂拷問:日誌會輸出什麼?
kotlin代碼:KtEqualMain.kt
fun stringEqual() {
val strBuilder1 = StringBuilder("IT互聯網大叔").toString()
val strBuilder2 = StringBuilder("IT互聯網大叔").toString()
println("strBuilder1 == strBuilder2 :" + (strBuilder1 == strBuilder2))
val str1 = "IT互聯網大叔"
val str2 = "IT互聯網大叔"
println("str1 == str2 :" + (str1 == str2))
}
複製代碼
大叔的靈魂拷問:日誌會輸出什麼?
咱們來看一段Java代碼:
public static void close(Object obj) throws IOException {
//已經判斷他是Closeable,依然須要強轉
if(obj instanceof Closeable){
((Closeable) obj).close();
}
}
複製代碼
咱們再來看看kotlin
@Throws(IOException::class)
fun close(obj: Any) {
if (obj is Closeable) {
obj.close()//obj已是一個Closeable類型了
}
}
複製代碼
咱們繼續
再繼續
上面這種寫法,java代碼段是否是看的有點迷茫,看不懂,你忙就對了,大叔也迷茫。
java對泛型的強轉,已經嚴重影響到,代碼的可讀性了。
kotlin的類型轉換,對代碼的可讀性提高了太多。
val max = if (a > b){
print("a is bigger")
a
} else if(a == b){
print(" a == b")
a
}else {
print("b is bigger")
b
}
//....
複製代碼
上面代碼也能夠用when表達式,實現:
val max = when {
a > b -> {
print("a is bigger")
a
}
a == b -> {
print(" a == b")
a
}
else -> {
print("b is bigger")
b
}
}
複製代碼
比java的 三元運算符(條件 ? 而後 : 不然),if表達式可讀性要更高,對初學者也更友好。
並且比 三元運算符 更靈活。
另外,java14的switch也變成表達式了。
對了kotlin的try catch,也能夠當表達式用。
什麼是解構申明?解構申明能幹什麼?
咱們先來看看,java是怎麼遍歷Map的。
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
map.put("two", 2);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + "->" + entry.getValue());
}
複製代碼
上面的代碼,全部java程序員都很是熟悉。
不是大叔吹牛,從大學,到如今,大叔寫過的for循環遍歷,比你家娃,吃過的鹽還多。
能夠說這種對Map遍歷的方式,已經完全紮根在個人思惟裏。我以爲全天下全部程序都應該是這麼遍歷的。
直到有一天,大叔碰見了kotlin的解構申明:
val map = mapOf("one" to 1, "tow" to 2)
for ((key, value) in map) {//經過解構申明來遍歷Map
println("$key->$value")
}
複製代碼
一聲直呼,我靠,還能這樣遍歷Map。
這纔是人的思惟方式啊,兄弟們啊啊啊。。。。
這種語法是如此的親切簡潔。基本上都不用學習,看一眼就懂這是什麼意思。
想一想剛學編程的那會,學c語言,一個for循環遍歷就能學一週。。。。
接着咱們找到Maps.kt的源碼:
kotlin正是經過 component1() 和 component2() 兩個函數實現了Map.Entry類的解構申明。
接着咱們來看一個代碼段:
fun main() {
val people = People("IT互聯網大叔", 18)
var (myName, myAge) = people//注意這行代碼,解構申明
println("my name is $myName, i am $myAge")
}
class People(val name: String, val age: Int) {
operator fun component1() = name //注意
operator fun component2() = age //注意
}
複製代碼
由於People重載了函數component1()和component2(),因此能夠將其解構成name和age。
咱們一塊兒看看kotin是怎麼作到的吧。
咱們先來看看他的字節碼吧。
什麼,字節碼?大叔別逗我了。這怎麼看?
看起來麻煩?不容易理解。
嗯,大叔很是贊成。
因此大叔準備了另外一份代碼段,把上面的字節碼反編譯成java代碼。
這種解構申明的語法,本質是個語法糖,在編譯器作了一些優化。
只是編譯器幫咱們,調用了people.component1()並幫咱們賦值了。
String myName = people.component1();
int myAge = people.component2();
複製代碼
衆所周知,java的函數只能有一個返回值。不能同時返回多個值。
可是有了解構申明以後,咱們能夠僞裝,函數能返回多個值,以下代碼:
fun main() {
val (myName, myAge) = getFirstPeople()//返回值爲People,咱們能夠直接把返回值結構
println("my name is $myName, i am $myAge")
}
fun getFirstPeople() = People("IT互聯網大叔", 18)
複製代碼
咱們知道java裏面是沒有無符號整型的,可是kotlin有,哈哈哈。
kotlin.UByte: 無符號 8位,範圍是 0 到 255
kotlin.UShort: 無符號 16位,範圍是 0 到 65535
kotlin.UInt: 無符號 32位,範圍是 0 到 2^32 - 1
kotlin.ULong: 無符號 64位,範圍是 0 到 2^64 - 1
複製代碼
大叔的自我拷問 :這能解決啥痛點,勞資java寫了這麼多年了,沒有無符號整形,不是照樣寫的好好的麼。
大叔的自我拷問 :這能解決啥痛點,勞資java寫了這麼多年了,沒有無符號整形,不是照樣寫的好好的麼。
大叔的自我拷問 :這能解決啥痛點,勞資java寫了這麼多年了,沒有無符號整形,不是照樣寫的好好的麼。
在痛點3.4 解構申明 裏面咱們寫了一個類People:
class People(val name: String, val age: Int) {}
複製代碼
咱們的age是int類型。int是能夠是負數的。
可是 「年齡」 這種東西有負數嗎?
可是 「年齡」 這種東西有負數嗎?
可是 「年齡」 這種東西有負數嗎?
咱們把age傳進來的時候,是否是要作個數據檢查。
假若有同事傳了個負數,致使程序出bug,就只能怪本身不嚴謹。
相似,打破你的認知,java,除以0必定會崩潰嗎? blog裏描述的bug。
因而咱們改造了下代碼,咱們先假如kotlin沒有無符號整型:
class People {
private val name: String
private val age: Int
constructor(name: String, age: Int) {
this.name = name
if (age >= 0) {
this.age = age
} else {
this.age = 0
println("illegal age $age")
}
}
}
複製代碼
UInt類型,自從有了你,世界變得好美麗 ------ ˙這句要唱出來
class People(val name: String, val age:UByte) {}
複製代碼
不只更優雅並且更健壯。
好了,聊完了技術。大叔要開始碎碎唸了。
前兩天,有位很資深的老鳥,年紀比大叔還要大,跟大叔聊天。
資深的老鳥:「我作的這個需求,要延期了「
大叔:「怎麼了?」
資深的老鳥:「這個模塊,以前的實現大量使用了LiveData,我還沒用過LiveData,並且,他用的好複雜,代碼好難看懂啊……」
大叔:「額,那你打算怎麼弄?」
資深的老鳥:「我打算,重寫一下這個模塊,xxx同事,這個LiveData用的太複雜了,一個很簡單的邏輯,寫的好複雜啊……我改不動……」
資深的老鳥:「哎,這種團隊還沒普及的技術,最好不要隨便亂用吧,並且他用的很複雜,一個很簡單的功能,繞來繞去的……搞的如今項目緊張狀況下,讓我難受……」
大叔:「……」
大叔內心其實挺爲這位同事捏把汗的,我以爲,他選擇了一個很冒險的方式。在這種緊急的狀況下,重寫一個模塊,每每風險只會更高。
掘友們,大家怎麼看呢?若是你是老鳥,你會怎麼選擇?
讚美是一種美德,點個贊 再走啊,老鐵。💖大叔須要你的贊👍