google爲什麼選擇kotlin?kotlin如何解決java開發痛點【續】?

上篇,kotlin如何解決java開發痛點,讓程序員happier 寫的很長,大叔覺得在快餐式學習的時代,沒幾我的會看到最後。沒想到,看完整篇的掘友還很多。html

@懶洋君 鼓勵大叔,讓大叔再寫一篇,多寫幾個痛點,說是會來給大叔點贊,但願不是騙大叔的~ 哈~java

今天這篇比上篇還長,你還會看完整篇嗎?哈哈哈~~python

1、google爲什麼選擇kotlin?

咱們先來聊點有趣的八卦:谷歌爲什麼選擇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公司和谷歌公司的實錘資料。請你們保持獨立思考。

2、kotlin如何解決java開發痛點【續】?

咱們進入今天的第二個主題。繼續一個個語法特性來說,kotlin如何解決java開發痛點。

沒有看上一篇 kotlin如何解決java開發痛點,讓程序員happier 的朋友們,能夠先看下上一篇,這樣會更有連續性。

2.1 操做符:== 和 ===

咱們先從一道java考題入手。大叔仍是一位亮仔的時候,被這道題折磨的痛並快樂着,哈哈~

大多數 java老鳥 都認識這道題。

JavaEqualMain.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的代碼

KtEqualMain.kt

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?

爲何?

kotlin 提供了兩個相等操做符 == 和 ===

結構相等 ==:用 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))
}
複製代碼

大叔的靈魂拷問:日誌會輸出什麼?

2.2 智能類型轉換(Smart Casts)

咱們來看一段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的類型轉換,對代碼的可讀性提高了太多。

2.3 if、when表達式

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,也能夠當表達式用。

2.4 解構申明(Destructuring Declarations)

什麼是解構申明?解構申明能幹什麼?

2.4.1 經過解構申明,遍歷Map

咱們先來看看,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();
複製代碼
3.4.2 經過解構申明,實現函數返回多個值

衆所周知,java的函數只能有一個返回值。不能同時返回多個值。

可是有了解構申明以後,咱們能夠僞裝,函數能返回多個值,以下代碼:

fun main() {
  val (myName, myAge) = getFirstPeople()//返回值爲People,咱們能夠直接把返回值結構
  println("my name is $myName, i am $myAge")
}

fun getFirstPeople() = People("IT互聯網大叔", 18)
複製代碼

3.五、無符號整型

咱們知道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) {}
複製代碼

不只更優雅並且更健壯。

3、結尾

好了,聊完了技術。大叔要開始碎碎唸了。

前兩天,有位很資深的老鳥,年紀比大叔還要大,跟大叔聊天。

資深的老鳥:「我作的這個需求,要延期了「

大叔:「怎麼了?」

資深的老鳥:「這個模塊,以前的實現大量使用了LiveData,我還沒用過LiveData,並且,他用的好複雜,代碼好難看懂啊……」

大叔:「額,那你打算怎麼弄?」

資深的老鳥:「我打算,重寫一下這個模塊,xxx同事,這個LiveData用的太複雜了,一個很簡單的邏輯,寫的好複雜啊……我改不動……」

資深的老鳥:「哎,這種團隊還沒普及的技術,最好不要隨便亂用吧,並且他用的很複雜,一個很簡單的功能,繞來繞去的……搞的如今項目緊張狀況下,讓我難受……」

大叔:「……」

大叔內心其實挺爲這位同事捏把汗的,我以爲,他選擇了一個很冒險的方式。在這種緊急的狀況下,重寫一個模塊,每每風險只會更高。

掘友們,大家怎麼看呢?若是你是老鳥,你會怎麼選擇?






讚美是一種美德,點個贊 再走啊,老鐵。💖大叔須要你的贊👍

相關文章
相關標籤/搜索