###Kotlin簡介
####Kotlin是什麼
Kotlin是JetBrains公司開發的一門語言,一聖彼得堡附近的Kotlin島嶼來命名的。Kotlin是執行在JVM上面的一門靜態強型別語言,可以編譯成JavaScript原始碼,執行在瀏覽器上,與Java100%相容。
####Kotlin的特性
為什麼有了Java還需要有Kotlin?下面就看看Kotlin的一些特性吧:
- 空型別安全
- Lambda表示式
- 擴充套件方法
- 型別推導
- 勝任Java能做的所有事情,使用起來比Java簡單,例如沒有句末分號
####Kotlin相關參考資料
###Kotlin環境搭建
####Kotlin練習環境搭建之--Hello Word!
下面我們先用IDEA來建立一個專案,開啟IDEA,建立專案,選擇Kotlin(JVM),如下圖所示:
######Tips:如果沒有Kotlin選項,請先安裝Kotlin外掛,建立專案需要選擇lib庫。 ######Tips:首次建立專案需要Index一兩分鐘。
與Java一樣,在src目錄下面建立一個Package,建立.kt檔案:
fun main(args: Array<out String>) {
print("Hello Word\n")
//建立類的時候不需要new關鍵字了
print(MyBean(1, "test bean"))
}
//Kotlin風格的資料物件
data class MyBean(var id: Int, var name: String)
複製程式碼
那麼程式就會輸出:
Hello Word
MyBean(id=1, name=test bean)
複製程式碼
####Kotlin練習環境搭建之--使用Gradle搭建環境
Gradle是一個依賴管理的工具,以前我們都是直接把jar包等原始碼直接拷貝進來的,但是這樣很麻煩。有了Gradle之後,我們就可以通過指令碼或者圖形化介面進行依賴管理了,對以後依賴庫的升級維護也很方便。
同樣用IDEA進行開發,下面我們建立一個專案,選擇Gradle、Kotlin,如下圖所示:
接下來配置構件資訊,其中包括代表公司或者組織的GroupId,代表自己在公司或者組織裡面的代表自己的ArticleFactId,然後就是構件的版本:
然後就是配置Gradle,這裡筆者使用自己本地的Gradle,然後選擇生成一些重要的資料夾,例如src/main/java目錄:
專案建立好之後如下:
工具已經幫我們建立好需要的目錄了。我們下面來看一些與Gradle相關的重要檔案:
settings.gradle:是存放每個module的資訊的,其內容如下:
rootProject.name = 'KotlinDemo'
複製程式碼
build.gradle,與build相關的指令碼,其內容如下:
group 'nan.com'
version '1.0-SNAPSHOT'
buildscript {
repositories {
//下面的dependencies中,外掛的地址
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.4"
}
}
//標識Java與Kotlin工程,存放著與編譯相關的一系列指令集
apply plugin: 'java'
apply plugin: 'kotlin'
//標識Java的版本,這裡沒有使用到
sourceCompatibility = 1.5
repositories {
//module中的依賴的地址
jcenter()
}
//Gradle中最重要的功能--依賴管理
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.4"
testCompile group: 'junit', name: 'junit', version: '4.11'
}
複製程式碼
####使用Gradle進行依賴管理
由於Kotlin中,反射包是獨立與基礎包的,因此我們以反射這個包為例子,介紹一下使用Gradle進行依賴管理。
修改build.gradle:
//Gradle中最重要的功能--依賴管理
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.4"
//增加反射依賴
compile "org.jetbrains.kotlin:kotlin-reflect:1.0.4"
testCompile group: 'junit', name: 'junit', version: '4.11'
}
複製程式碼
然後就可以在程式碼中進行反射操作了,例如我們拿到Person類的構造方法:
fun main(args: Array<String>) {
Person::class.constructors.map {
print(it)
}
//也可以通過程式碼提示直接改成這種寫法
Person::class.constructors.map(::print)
}
data class Person(var id: Int, var name: String) {
}
複製程式碼
###Kotlin空指標安全
我們先來看一個例子,我們先建立一個Person類:
data class Person(var id: Int, var name: String) {
}
複製程式碼
然後在Java程式碼中new這個類:
public class Test { public static void main(String[] args) { //注意第二個引數傳了null Person p = new Person(1, null); } }
執行的時候發現報錯:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method Person.<init>, parameter name
at Person.<init>(Test.kt)
at Test.main(Test.java:7)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
複製程式碼
因為Kotlin的類中實質上加了@notnull註解。
同樣地,我們在Kotlin中使用這個Person,也傳個null進去:
fun main(args: Array<String>) {
val p = Person(1, null)
}
複製程式碼
你會發現,根本就編譯不過。這就是Kotlin的型別安全的體現了,把一些空指標的問題提前到了編譯階段,而不是執行階段,這一點真的很重要。
######Tips:可以通過code -- Conver Java File to Kotlin File或者直接貼上,把Java程式碼轉換Kotlin程式碼。
如果你的Person類中的name可以傳null的話,就需要加上"?"識別符號,完整的示例如下:
fun main(args: Array<String>) {
//不會報錯了
val p = Person(1, null)
}
data class Person(var id: Int, var name: String?) {
}
複製程式碼
###Kotlin中的集合
####Kotlin中的集合
與Java中用[]代表集合不一樣,在Kotlin中用Array表示集合,例如:
fun main(args: Array<String>) {
}
複製程式碼
與Java類似,可以在泛型中指定型別應該繼承哪個型別,Java中用extends關鍵字,在Kotlin中用out關鍵字,例如:
fun main(args: Array<out Bean>) {
}
複製程式碼
####集合的基本遍歷
下面是Kotlin中集合遍歷的集中方法:
fun main(args: Array<String>) {
for (arg in args) {
print(arg)
}
args.map({ print(it) })
args.map() {
print(it)
}
args.map { print(it) }
args.map(::print)
}
複製程式碼
######Tips:命令列引數可以在run--Edit Configuration中修改。
-
第一種沒有什麼好說的,與Java類似。
-
第二種開始,使用了map方法進行遍歷,map方法是Kotlin中的Arrays類提供的遍歷集合的函式,定義如下:
//map方法是一個擴充套件方法,只有一個Lambda表示式引數,接收的是一個T型別的引數,返回R型別。T型別就是我們要迭代的String型別,而R型別沒有明確指定 public inline fun <T, R> Array<out T>.map(transform: (T) -> R): List<R> { return mapTo(ArrayList<R>(size), transform) } 複製程式碼
因此呢,map方法可以傳進去一個 {表示式} 作為引數。而print接收一個it引數,it是迭代的String,返回的是空型別(Kotlin中空型別是Unit),完全符合map中的Lambda表示式的要求。因此可以傳進去。
- 第三種,既然map只有一個Lambda表示式引數,那麼可以把大括號後置,這是第三種。
- 第四中,在第三種的基礎上,既然大括號可以省略,那麼前面的()也省略了。
- 第五種,直接把函式的引用傳進去(通過兩個分號::)
######Kotlin中的空用Unit型別表示,無返回值的函式可以這樣寫(一般無返回值直接省略而已):
fun main(args: Array<String>) :Unit{
}
複製程式碼
####使用flatMap扁平化集合
現在有一個需求:
將apple_bus_cat dog_egg_fly good_hook_it
扁平化輸出為:apple:5 bus:3 cat:3 dog:3 egg:3 fly:3 good:4 hook:4 it:2
複製程式碼
在Java中是這樣做的:
public static void main(String[] args) {
for (String arg : args) {
String[] split = arg.split("_");
for (String s : split) {
System.out.print(s + ":" + s.length() + " ");
}
}
}
複製程式碼
在Kotlin中是通過flatMap進行扁平化,然後再通過map進行遍歷輸出的:
fun main(vararg args: String) {
args.flatMap {
it.split("_")
}.map {
print("$it:${it.length} ")
}
}
複製程式碼
程式中我們用到了flatMap對集合進行了一次扁平化,返回了一個陣列,最終丟給map。下面我們來看看flatMap的原始碼:
//實際上flatMap也是傳入一個Lambda表示式,將T型別(String)的資料轉換成可迭代的R型別資料,因此String的split符合要求
public inline fun <T, R> Array<out T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
//flatMap最終會呼叫flatMapTo方法,對集合進行扁平化,把每一個轉換出來的資料都放到一個列表裡面,因此,雖然我們有apple_bus_cat dog_egg_fly good_hook_it三組資料,但是最終被切割成apple bus cat dog egg fly good hook it這樣的一個陣列,然後再通過map進行遍歷輸出即可
public inline fun <T, R, C : MutableCollection<in R>> Array<out T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}
複製程式碼
######Tips:Kotlin中的可變長引數是用vararg關鍵字來宣告的。 ######Tips:與JS類似,雙引號""字串的拼接中可以使用$符號。 ######Tips:由於這裡迭代中需要傳參,因此不可以省略成args.map(::print)這種形式了(這種形式預設是把it傳進print方法中了)