iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 23
1

寫在前面

Kotlin是由俄羅斯團隊jetbrains推出的語言
運行於JVM(java虛擬機)上,並且兼容於Java,
這意味著Kotlin可以使用各大企業原本既有的Java函式庫,不必因為切換語言而修改舊有的環境

kotlin推出目的是為了改進Java冗長的程式碼,並且降低了新手學習的難度
目前被google指定為Android平台上的官方開發語言

google會這麼做的理由是為了避免自己的Android平台被對手掌握生死
Java現在的擁有者甲骨文公司曾經為了Android過多的使用Java的API而控告google
因此google急於尋找可以替代Java的語言,最終落腳於Kotlin

如果你需要,Kotlin也能轉成js,方便你執行於沒有JVM的電腦上

docker準備

這段是給打算架在docker上的學習者看的
如果你打算直接在本機上安裝kotlin編譯環境可以跳過這段,前往這裡

首先,你要知道目前(2020.08.31)dockerhub是沒有直接的kotlin image可以用的
必須自己包裝或是使用其他人放出來的
趕著學習語言的人可以使用我包好的image

docker pull skynocover/kotlin20-08:ubuntu

拉下來後就可以前往這裡

還留下來的人,我們來學如何自己包一個kotlin的image吧

首先,我們需要先抓一台ubuntu下來

docker pull ubuntu:latest

之後進入container

sudo docker run -it ubuntu:latest

並且開始安裝kotlin相關套件

apt-get update
apt-get install wget
apt-get install unzip
apt-get install zip
apt-get install curl
apt-get install openjdk-8-jre-headless
  • apt-get是ubuntu的更新套件
  • update是用來更新套件庫的
  • install則是安裝,這裡我們安裝了一些安裝kotlin會用到的套件以及openjdk

之後使用

wget -O sdk.install.sh "https://get.sdkman.io"
source sdk.install.sh
source ~/.sdkman/bin/sdkman-init.sh
sdk install kotlin

安裝kotlin

這樣我們現在這個container就已經安裝好kotlin了
不過還記得之前說過container就像是正在執行的光碟,是沒有紀錄檔的喔
因此我們要把現在這個執行中的container給燒成image

先使用

exit

退出docker

之後使用

sudo docker container ls -a
//或是
sudo docker ps -a

確認剛才的container

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS 
4f434a97fd25        ubuntu:latest       "/bin/bash"         16 minutes ago      Exited (0) 44 seconds ago

然後就可以使用

sudo docker commit 4f434a97fd25 kotlin:ubuntu

commit後面是container ID,每個人應該都不一樣,你只需要使用自己的containerID即可
後面的kotlin則是image的REPOSITORY,冒號之後才是tag,你可以替換成自己喜歡的

之後使用

docker images

就可以看到剛才自己建立的image拉

REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
kotlin                   ubuntu              14ac719e18ab        3 seconds ago       366MB

接下來我們進入程式碼的部份吧

程式碼

在你的工作目錄下面建立一個附檔名為kt的檔案
之後貼上

fun main() {
    println("Tell me what you want to do:")
    println("(1)T to H (2)H to T")

    val choose = readLine()!!
    when (choose) {
        "1" -> {
            println("Please enter the number:")
            val input = readLine()!!
            println(t2h(input))
        }
        "2" -> {
            println("Please enter the number:")
            val input = readLine()!!
            println(h2t(input))
        }
        else -> println("Wrong Select")
    }
}

fun h2t(input: String):Int{
    var output = 0 //不可用val因為會改變
    var i = 0
    while (i<input.length){
        output += AEreturn(input.substring(i,i+1))*(Math.pow(16.0,(input.count()-i-1).toDouble())).toInt()
        i++
    }
    return output
}

fun AEreturn(input: String):Int{
    try{
        return input.toInt()
    }
    catch(e:NumberFormatException){
        when (input) {
           "A" -> return 10
           "B" -> return 11
           "C" -> return 12
           "D" -> return 13
           "E" -> return 14
           "F" -> return 15
           else -> return 0
        }
    }
}

fun t2h(input : String):String{
   val num = input.toInt()
    for (i in 0..15) {
        for (j in 0..15) {
            for (k in 0..15){
                if ((Math.pow(16.0,2.0).toInt()*i+Math.pow(16.0,1.0).toInt()*j+Math.pow(16.0,0.0).toInt()*k)==num){
                    return (returnAE(i)+returnAE(j)+returnAE(k))
                }
            }
        }
    }
    return ""
}

fun returnAE(input:Int):String{
    if (input < 10){
        return input.toString()
    }
    when (input){
        10 -> return "A"
        11 -> return "B"
        12 -> return "C"
        13 -> return "D"
        14 -> return "E"
        15 -> return "F"
        else -> return ""
    }
}

之後使用你剛才製作的image來執行kotlin的程式碼吧

首先,先掛載你的工作目錄,並切換到掛載的資料夾

docker container run --rm -it -v ${PWD}:/home/kotlin kotlin:ubuntu /bin/bash
cd /home/kotlin/

之後,由於kotlin跟java一樣是需要先編譯後才能上JVM執行的語言
因此使用編譯指令如下

kotlinc convert.kt -include-runtime -d convert.jar

最終會在你的工作目錄下產生convert.jar檔
在JVM執行他吧

java -jar convert.jar

結構

kotlin各方面來說都取自於Java,因此結構上跟Java類似
需要有程式進入點

fun main(){

}

行尾則如同大多數新世代的語言一樣不需要;

輸入輸出

相較於Java冗長的語法

System.out.println("Tell me what you want to do:");
System.out.println("(1)T to H (2)H to T");
Scanner scanner = new Scanner(System.in);
String choose = scanner.nextLine();

kotlin簡潔許多

println("Tell me what you want to do:")
println("(1)T to H (2)H to T")

val choose = readLine()!!

不需要為了讀取參數而特地建立一個Scanner物件
(雖然Java這樣寫比較符合物件導向,但是在工作上來說卻又顯得過於多餘)

val是kotlin宣告變數的方式,這種方式會告訴kotlin此變數為固定值,不會改變
如果你需要隨著程式執行而改變的變數需要使用var

讀取輸入的readLine()!!後面的!!表示強制轉型
當你已經確保輸入值不會是null時就可以使用!!,當今天有任何原因輸入了null,就會導致程式crash
這樣雖然會導致你的程式停止,但是至少不會給出錯誤的結果(如果會給出錯誤的答案不如不給)
詳細可以看這裡

只是今天我們在這裡的用法兩者會是一樣的結果

邏輯when

在kotlin裡的switch是使用when這個關鍵字

when (choose) {
    "1" -> {
        println("Please enter the number:")
        val input = readLine()!!
        println(t2h(input))
    }
    "2" -> {
        println("Please enter the number:")
        val input = readLine()!!
        println(h2t(input))
    }
    else -> println("Wrong Select")
}

when的條件需要使用()包起來,
但是不用每個條件式執行完不用break
沒有符合的條件則使用else去接回來

還有這種寫法

when {
    x > y -> print("x > y") //若true則執行
    y > x -> print("y > x")
    else -> print("x = y")
}

這樣可以更好的取代if-elseif的寫法,讓你的程式碼看起來更加簡潔

之後我們再次詢問使用者需要轉換的數值並且讀取使用者的輸入

println("Please enter the number:")
val input = readLine()!!
println(t2h(input))

方法的結構

kotlin的方法關鍵字為fun
結構如下

fun 方法名稱(變數名稱:變數型別):回傳值型別{

}

由於演化自Java,因此跟Java一樣是靜態強型別的語言
因此要事先定義好輸入值及回傳值的型別

不過與Java不同的是省略了冗長的語法,
還記得Java的方法怎麼宣告嗎?

public static int h2t(String input){}

相較吃下kotlin這麼寫

fun h2t(input: String):Int{}

整體而言宣告的結構上更接近於golang

func h2t(number string)float64{}

迴圈while

我們往下看到h2t這個方法吧

fun h2t(input: String):Int{
    var output = 0 //不可用val因為會改變
    var i = 0
    while (i<input.length){
        output += AEreturn(input.substring(i,i+1))*(Math.pow(16.0,(input.count()-i-1).toDouble())).toInt()
        i++
    }
    return output
}

這邊複習一下如果你需要變動的變數
(比方說這裡我們要累加output)
那就需要使用var而不是val這個關鍵字

在kotlin中while的結構跟Java類似,都是使用

while (條件句){
    //執行句
}

字串處理

這邊注意一點,在Java中,取得字串的長度是使用方法length(),而kotlin則是取用屬性

java這樣寫

input.length()

kotlin這樣寫

input.length

用法上有點不一樣

但是字串切割的方式就跟Java一樣了

input.substring(i,i+1)

相較於字串使用Java提供的方法,kotlin在冪次的計算上就麻煩許多了

Math.pow(16.0,(input.count()-i-1).toDouble())

在Java,計算冪次時輸入值會自動幫你轉型,
但是kotlin的預設輸入型別為double,並且不會自動幫你轉型,16.0是我們隱性宣告為double的方式
(就是不指定他,kotlin會自動判定有小數點的數值型別為double)
但是input.count()-i-1是int型別,必須手動轉成double後才可以放入pow內計算冪次

冪次計算上還有一些要注意的地方

迴圈for

我們往下看到與h2t相對的t2h方法吧

fun t2h(input : String):String{
   val num = input.toInt()
    for (i in 0..15) {
        for (j in 0..15) {
            for (k in 0..15){
                if ((Math.pow(16.0,2.0).toInt()*i+Math.pow(16.0,1.0).toInt()*j+Math.pow(16.0,0.0).toInt()*k)==num){
                    return (returnAE(i)+returnAE(j)+returnAE(k))
                }
            }
        }
    }
    return ""
}

迴圈for表達的方式跟Rust及Ruby類似,都是使用

i in 起點..終點

的方式,
kotlin的循環語句必須使用()包起來,並且循環次數包含終點(這裡的意思就是會包含15)

邏輯if

kotlin的邏輯if跟Java一樣

if(條件句){
    //執行句
}

詳細我們就不說了

錯誤處理

kotlin的錯誤處理方式跟Java一樣,差別只在於catch

catch(e:NumberFormatException){}

由於kotlin的型別放在變數後面,因此是這樣寫

原生的Java長這樣

catch(NumberFormatException e){}

差別只在於變數跟型別的順序

以上就是kotlin的基本語法拉

小結

來複習今天學的語法吧

  • 程式結構
    • 比起Java更接近golang
    • 行尾不需要;
  • 印出/讀取
    • 輸出使用println
    • 輸入使用readLine()!! 不用經過實例即可使用
  • 變數宣告
    • var 表示變數可變
    • val 表示變數宣告完便不會再變動
  • 邏輯控制
    • when是以前的switch關鍵字
    • if 跟Java一樣
  • 迴圈控制
    • for 跟Java一樣
    • while 跟Java一樣
  • 方法定義
    • 靜態型別的語言,必須事先定義好輸入值與輸出值的型別
  • 異常處理
    • 除了型別順序的問題外,都跟Java一樣

kotlin簡化了以前在Java時常會感到厭煩的問題(冗長的程式碼,過多的宣告與實例化)
與其他挑戰Java的語言不同的是kotlin保留跟Java的溝通性
如同現在新世代的語言都會留有跟C/C++溝通的方式一樣

因此作為google指定Android的語言還是可以期待他未來的發展
或者說kotlin的發展性要看google對Dart的期待程度
畢竟如果要離開JVM,還是需要編譯成各平台的可執行檔
為此,google開發了Dart以及他的flutter框架

因此,我們明天來學最後一個語言,Dart吧


上一篇
F# 被遺忘的優秀語言
下一篇
Dart 食之無味,棄之可惜
系列文
你會十五種程式語言?不,我會十五種HelloWorld.為了避免這種狀況,因此寫了這篇:淺入淺出十五種程式語言30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
skycover
iT邦新手 4 級 ‧ 2020-09-23 20:16:22

如果有任何寫不清楚或是觀念沒有很明白的話請留言告知我
會盡快補上

如果有任何寫錯的地方也麻煩留言告知我
會盡快修正

感謝各位

我要留言

立即登入留言