1. 程式人生 > >kotlin的基本用法詳解

kotlin的基本用法詳解

基本用法
1.常量和變數
使用val關鍵字宣告一個常量(只讀,不可修改),使用var關鍵字宣告一個變數,下面是具體用法:

  1. fun test() {  
  2.     //使用val關鍵字宣告一個常量(只讀),宣告常量時必須初始化  
  3.     val a: Int = 1 //顯式指定常量的型別  
  4.     val b = 2  //自動推斷型別  
  5.     val c: Int //宣告一個不初始化的常量,必須顯式指定型別  
  6. //    b = 3  //常量值不可修改,這句程式碼會報錯  
  7.   
  8.     //a = 3  //不可以修改常量的值,此句程式碼會報錯  
9. 10. //使用var關鍵字宣告一個變數,變數的值可以修改 11. var year: Int = 2016 //顯式指定變數的型別 12. var month = 5 //自動推斷變數型別 13. var day: Int //宣告一個不初始化的變數,必須顯式指定型別 14. month = 6 //變數值可以被修改 15. } 基本型別 對於java中我們都很清楚基本型別有 byteshortintlongdoublefloat、boolean、char 等,對於Kotlin中當然也有與其對應的,不過它們更像是java中的包裝類 比如int
->Integer ,Kotlin中是Int 下面是對應關係(首字母變大寫) - int->Int - double->Double - long->Long - double->Double - float->Float - boolean->Boolean - char->Char Kotlin中使用接近於Java的方式(內建型別)來表示數字,但是又不完全相同, 比如沒有隱式轉換!Kotlin中數字相關的內建型別如下: 需要注意幾點: ● 1.沒有自動向上轉型,比如Int轉Long,需要自己調toXxx方法轉 ● 2.
Long型別結尾必須為大寫的L,不能為小寫,比如1024L ● 3.字元Char不是Number,用單引號來宣告,比如'c',不能像Java一樣直接拿來當數字使, 如果你想把Char的值給Int,需要調toInt()方法 ● 4.Boolean的值為truefalse5.Kotlin不支援8進位制,十六進位制0x開頭,二進位制0b開頭 ● 6.位運算子,Java中的與或運算子用:|和&,kotlin中使用or和and關鍵字來替代 其他運算子也有分別的關鍵字替代:shl(有符號左移),shr(有符號右移),ushr(無符號右移) ,xor(按位異或),inv(按位取反) ========================================================================================2、函式(方法)的使用 定義函式使用 fun 關鍵字,如下程式碼所示: 1. fun add(a: Int, b: Int): Int { 2. return a + b 3. } 函式add有兩個Int型的引數,冒號後跟的是函式的返回值,一條程式碼語句的末尾不用加分號,當然加上分號也沒有問題。 上面的add函式還可以簡寫成如下形式: 1. fun add(a: Int, b: Int) = a + b; 沒有顯式指定函式的返回值,會自動推斷函式的返回值。 如果一個函式沒有返回值,可以寫成如下兩種形式: 1. //沒有返回值的函式,顯式指定Unit為返回值 2. fun showAddResult(a: Int, b: Int): Unit { 3. println(a + b) 4. } 5. 6. //沒有返回值的函式,省略Unit的寫法 7. fun showAddResult2(a: Int, b: Int) { 8. println(a + b) 9. } ========================================================================= 4、字串模板 Kotlin允許在字串中嵌入變數和表示式,只用在字串內用 $ 符號開頭,隨後跟上輸出變數的變數名即可,例如: val name = "Bob" println("My name is ${name}") //列印"My name is Bob" val a = 10 val b = 20 println("The sum is ${a+b}") //列印"The sum is 30" 你也可以用下面的表示式: val apples = 4 val bananas = 3 println("I have $apples apples and " + (apples + bananas) + " fruits.") // Java-esque println("I have $apples apples and ${apples+bananas} fruits.") // Kotlin //args 表示陣列名稱 Array<String>表示字串型別的陣列 ${args[0]} 表示陣列中第0個元素的值 1. //字串模板的用法 t? 2. fun stringTempl(args: Array<String>) { 3. if(args.size > 0) 4. println("args[0] = ${args[0]}") 5. } 6. 7. //main方法是整個程式的入口 8. fun main(args: Array<String>){ 9. var arr = arrayOf("hello", "world") 10. stringTempl(arr) 11. } 上面的程式碼執行後,在控制檯列印如下內容: 我們來檢查我們是否給 main 函式傳遞了引數。先來判斷這個字串陣列是不是空,如果不為空,我們把第一個字串分配給 name 變數。Kotlin 裡有個 val 型別的宣告方法,類似 Java 裡的 final,也就是常量。 fun main(args: Array<String>) { val name = "World" if (args.isNotEmpty()) { name = args[0] } println("Hello, $name!") } 在我們編譯這個程式的時候,我們遇到一個問題:無法重新分配新的值給一個常量。一種解決方法是用內聯的 if-else 方法。Kotlin 裡的多數的程式碼塊都支援返回值。如果語句進入了 if 程式碼塊兒,也就是說 args 非空,那麼就返回 arg[0],否則返回 “World”。 if-else 語句結束後,就直接賦值給我們之前宣告的 name 常量,下面的例子就是條件賦值程式碼塊: fun main(args: Array<String>) {. val name = if (args.isNotEmpty()) { args[0] } else { "World" } println("Hello, $name!") } 我們可以把上面的程式碼用一行來書寫,看起來有點像 Java 裡的三目運算子。移除掉那些大括號後,看起相當漂亮: val name = if (args.isNotEmpty()) args[0] else "World" 然後kotlin還支援字串遍歷 ====================================================================== 5、條件表示式 區間表示式 你可能在其他的語言裡見到過這樣的表示式。的確, Kotlin 的不少特性是借鑑自其他語言裡。下面這個表示式:如果 i 大於等於 1,並且小於等於 10,就將其打印出來。我們檢測的範圍是 110if (1 <= i && i <= 10) { println(i) } 其實我們可以用 intRange 函式來完成這個操作。我們傳入 110,然後呼叫 contains 函式來判斷是否在這個範圍裡。我們打印出 i 即可。 if (IntRange(1, 10).contains(i)) { println(i) } 這個還可以用擴充套件函式來實現,1.rangeTo 建立了一個 110 的 intRange,我們可以用 contain 來判斷它。 更完美的而簡潔的寫法,是用下面的操作符: if(i in 1..10) { ... } .. 是 rangeTo 的一個別名,它實際背後工作原理還是 rangeTo。 我們還可遍歷一個區間,比如:可以用 step 關鍵字來決定每次遍歷時候的跳躍幅度: for(i in 1..4 step 2) { ... } 也可以逆向迭代,或者逆向遍歷並且控制每次的 step: for (i in 4 downTo 1 step 2) { ... } 在 Kotlin 裡,也可以結合不同的函式來實現你想要的區間遍歷。可以遍歷很多不同的資料型別,比如建立 strings 或者你自己的型別。只要符合邏輯就行。 常規的條件表示式可以是這麼寫的: 1. //常規寫法的條件表示式,這裡的函式返回值不能省略 2. fun max(a: Int, b: Int): Int { 3. if(a > b) 4. return a 5. else 6. return b 7. } Kotlin可以簡寫條件表示式,如下所示: 1. //簡寫的條件表示式 2. fun max2(a: Int, b: Int) = if(a > b) a else b ================================================================== 6、可空型別 [plain] view plain copy print? 1. fun nullableTest() { 2. //在變數型別後面加上問號,代表該變數是可空變數 3. var name: String? = "zhangsan" 4. name = null //可以將null賦值給name變數 5. var person: String = "tom" 6. // person = null //這句程式碼會報錯,不可以將null賦值給一個不可空變數 7. } 函式返回值為可空的例子如下程式碼: [plain] view plain copy print? 1. /* 2. 函式返回值為Int?,表示返回值可為空 3. 當引數為空或者為""時,則返回null,否則使用Java中的字串轉整型的方法 4. 這裡也體現了kotlin程式碼和Java程式碼無縫整合 5. */ 6. fun parseInt(s: String): Int? { 7. if(s == null || s == "") 8. return null; 9. return Integer.parseInt(s); 10. } =============================================================== 7、型別檢查和自動型別轉換 Kotlin中使用is運算子來檢查資料型別和做型別轉換,如下程式碼所示: 1. /* 2. 當函式引數為字串型別時,就返回字串的長度,否則返回空 3. */ 4. fun getStringLength(n: Any): Int? { 5. if(n is String) 6. return n.length //這裡會自動將n轉化為字串型別 7. return null 8. } 上面的程式碼還可以寫成: 1. /* 2. 當函式引數為字串型別時,就返回字串的長度,否則返回空 3. */ 4. fun getStringLength(n: Any): Int? { 5. if(n !is String) 6. return null 7. return n.length //這裡會自動將n轉化為字串型別 8. } ====================================================================== 8for迴圈和while迴圈 1. //for迴圈的測試程式碼 2. fun testFor() { 3. var arr = arrayOf(1, 3, 4, 5, 6) 4. for(i in arr.indices) { //通過索引迴圈 5. println(arr[i]) 6. } 7. for(num in arr) { //直接使用陣列中的物件迴圈 8. println(num) 9. } 10. } 11. 12. //while迴圈的測試程式碼 13. fun testWhile() { 14. var i = 0; 15. while(i < 10) { 16. print(" " + i) 17. i++ 18. } 19. } ========================================================================= 9、when表示式 when表示式就類似於Java中的switch表示式,如下程式碼所示: 1. fun main(args: Array<String>) { 2. testCase("hello world") 3. } 4. 5. fun testCase(obj: Any) { 6. when(obj) { 7. is String -> { 8. print("this is string") 9. } 10. is Int -> { 11. print("this is integer") 12. } 13. else -> { 14. print("unkown value") 15. } 16. } 17. } ========================================================================== 10、ranges的使用 (1)使用in操作符檢查一個數是否在某個範圍內 1. /* 2. 判斷分數是否大於等於90,小於等於100 3. */ 4. fun isGood(score: Int) { 5. if(score in 90..100) //ranges是閉區間 6. println("very good") 7. else 8. println("not so good") 9. } (2)檢查索引是否越界 1. /* 2. 檢查index是否在陣列arr的索引範圍內 3. */ 4. fun checkIndex(index: Int, arr: Array<Int>) { 5. if(index in 0..arr.lastIndex) //arr.lastIndex返回的是陣列的最後一位的下標 6. println("index in bounds") 7. else 8. println("index out of bounds") 9. } (3)遍歷一個範圍 1. for(i in 1..5) { 2. println(i) 3. } 也可以通過in運算子遍歷一個集合,如下程式碼: 1. //in運算子遍歷一個字串陣列 2. fun testStr(arr: Array<String>) { 3. for(str in arr) 4. println(str) 5. } ================================================================================= 11、三元運算子 int length = a != null ? a.length() : -1 上面的程式碼你可能在 Java 裡見到過。用三目運算子取值,檢查是否為空,如果為空則返回真實的長度,否則返回 -1,Kotlin 裡又相同的實現: var length = if(a!= null) a.length() else -1 如果 a 不是 null, 那麼就可以直接讀值,否則返回預設值。這裡用 elvis操作符 實現的簡寫: var length = a?.length() ?: -1 我們用 ?號做了一個內聯空檢查。如果你還記得剛才我說的,如果 a 是 null,第一個 ?表示式就會返回 null ,如果 elivs 操作符 左側是空,那麼他就會返回右側,否則直接返回左側的值。 ============================================ 12、高階函式 很多語言已經支援了高階函式,比如 Java 8,但是你並不能用上 Java 8。如果你在用 Java 6 或者 Java 7,下面的例子實現了一個具有過濾功能的函式: public interface Function<T, R> { R call(T t); } public static <T> List<T> filter(Collection<T> items, Function<T, Boolean> f) { final List<T> filtered = new ArrayList<T>(); for (T item : items) if (f.call(item)) filtered.add(item); return filtered; } filter(numbers, new Function<Integer, Boolean>() { @Override public Boolean call(Integer value) { return value % 2 == 0; } }); 我們首先要宣告一個函式介面,接受引數型別為 T,返回型別為 R。我們用介面中的方法遍歷操作了目標集合,建立了一個新的列表,把符合條件的過濾了出來。 fun <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> { val filtered = arrayListOf<T>() for (item in items) if (f(item)) filtered.add(item) return filtered } 上面的程式碼是在 Kotlin 下的實現,是不是簡單很多?我們呼叫的時候如下: kotlin filter(numbers, { value -> value % 2 == 0 }) 你可能也發現了,我們沒有定義任何的函式介面,這是因為在 Kotlin 中,函式也是一種資料型別。看到 f:(T) -> Boolean 這個語句了嗎?這就是函式型別作為引數的寫法,f 是函式別名,T是函式接受引數,Boolean 是這個函式的返回值。定義完成後,我們隨後就能跟呼叫其他函式一樣呼叫 f。呼叫 filter 的時候,我們是用 lambda 表示式來傳入過濾函式的,即:{value ->value % 2 = 0}。 由於函式型別引數是可以通過函式宣告的簽名來推導的,所以其實還有下面的一種寫法,大括號內就是第二個引數的函式體: filter(numbers) { it % 2 == 0 } ============================================================= 13 .行內函數 行內函數和高階函式經常一起見到。在某些場景下,當你用到泛型的時候,你可以給函式加上inline 關鍵字。在編譯時,它會用 lambda 表示式替換掉整個函式,整個函式的程式碼會成為內聯程式碼。 如果程式碼是這樣的: inline fun <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> { val filtered = arrayListOf<T>() for (item in items) if (f(item)) filtered.add(item) return filtered } filter(numbers) { it % 2 == 0 } 由 inline 關鍵字在編譯後會變成如下這樣: val filtered = arrayListOf<T>() for (item in items) if (it % 2 == 0) filtered.add(item) 這也意味著我們能實現一些常規函式實現不了的。比如:下面這個函式接受一個 lambda 表示式,但並不能直接返回: fun call(f: () -> Unit) { f() } call { return // Not allowed } 但是如果我們的函式變成行內函數,現在我們就能直接返回了,因為它是行內函數,會自動和其他程式碼混合在一起: inline fun call(f: () -> Unit) { f() } call { return // Now allowed } 行內函數也允許用 reified 型別。下面這個例子就是一個真實場景下的函式,通過一個 View 尋找型別為 T 的父元素: inline fun <T : Any> View.findViewParent(): T? { var parent = getParent() while (parent != null && parent !is T) { parent = parent.getParent() } return parent as T // Cast warning } 這個函式還有些問題。由於泛型型別被擦除了,所以我們無法檢測型別,即便我們手工來做檢查,依然會出現 warning。 解決方案是:我們給函式引數型別加上 reified 關鍵字。因為函式會被編譯成內聯程式碼,所以我們現在就能手工檢查型別消除警告了: inline fun <reified T : Any> View.findViewParent(): T? { var parent = getParent() while (parent != null && parent !is T) { parent = parent.getParent() } return parent as T // Type cast allowed } =========================================================================== 14 .函式擴充套件 函式擴充套件是 Kotlin 最強大的特性之一。下面是一個工具函式,檢測 App 是否執行在 Lollipop 或者更高的 Api 之上,它接受一個整數引數: public fun isLollipopOrGreater(code: Int): Boolean { return code >= Build.VERSION_CODES.LOLLIPOP } 通過 被擴充套件型別.函式 的寫法,就能將函式變成被擴充套件型別的一部分,寫法如下: public fun Int.isLollipopOrGreater(): Boolean { return this >= Build.VERSION_CODES.LOLLIPOP } 我們不在需要引數,想要在函式體內呼叫整數物件需要用 this 關鍵字。下面就是我們的呼叫方法,我們可以直接在整數型別上呼叫這個方法: 16.isLollipopOrGreater() 函式擴充套件可以是任何整形,字面量或者包裝型別,也可以在標記為 final 的類上做類似操作。因為擴充套件函式不是真的給類增加程式碼,任何人都沒有辦法去修改一個類,它實際上是建立了一個靜態方法,用語法糖來讓擴充套件函式看著像是類自帶的方法一樣。 Kotlin 在 Java 集合中充分利用了擴充套件函式,這有一個例子操作集合: final Function<Customer, Order> customerMapper = // ... final Function<Order, Boolean> orderFilter = // ... final Function<Order, Float> orderSorter = // ... final List<Order> vipOrders = sortBy(filter(map(customers, customerMapper), orderFilter), orderSorter); 我們對一個 customer 集合,執行了 map, filter, 以及 sort 操作。巢狀的寫法混亂而且難以閱讀。下面是標準庫的擴充套件函式寫法,是不是簡潔了很多: val vipOrders = customers .map { it.lastOrder } .filter { it.total >= 500F } .sortBy { it.total } ================================================================================= 屬性 (30:55) Kotlin 把屬性也變成了語言特性。 class Customer { private String firstName; private String lastName; private String email; public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String getEmail() { return email; } public void setFirstName(String firstName) { this.firstName = firstName } public void setLastName(String lastName) { this.lastName = lastName } public void setEmail(String email) { this.email = email } } 上面是一個典型的 Java bean 類。可看到很多成員變數,和很多 getter, setter 方法,這可是隻有三個屬性的時候,就生成了這麼多程式碼。來看看 Kotlin 的寫法: class Customer { var firstName: String = // ... var lastName: String = // ... var email: String = // ... } 你只需要將成員變數定義成一個變數即可,預設是 public 的。編譯器會自動生成 getter 和setter 方法。 主建構函式 (31:49) Kotlin 中,類可以擁有多個建構函式,這一點跟 Java 類似。但你也可以有一個主建構函式。下面的例子是我們從上面的例子裡衍生出來的,在函式頭裡添加了一個主建構函式: 在主建構函式裡,可以直接用這些引數變數賦值給類的屬性,或者用構造程式碼塊來實現初始化。 class Customer(firstName: String, lastName: String, email: String) { var firstName: String var lastName: String var email: String init { this.firstName = firstName this.lastName = lastName this.email = email } } 當然,更好的方法是:直接在主建構函式裡定義這些屬性,定義的方法是在引數名前加上 var或者 val 關鍵字,val 是代表屬性是常量。 class Customer( var firstName: String, var lastName: String, var email: String) 單例 (35:53) 你可能經常會用到單例設計模式。比如一個 Logger 類,在 Java 裡,有多種實現單例的寫法。 在 Kotlin 裡,你只要在 package 級別建立一個 object 即可!不論你在什麼域裡,你都可以像單例一樣呼叫這個 objectobject Singleton 比如下面是一個 looger 的寫法: object Logger { val tag = "TAG" fun d(message: String) { Log.d(tag, message) } } 你可以直接通過 Logger.D 的方法來呼叫 D 函式,它在任何地方都是可用的,而且始終只有一個例項。 Companion Objects (37:00) Kotlin 移除了 static 的概念。通常用 companion object 來實現類似功能。你可能時常會看到一個 Activity 有一個 靜態型別的 string,名叫 tag,和一個啟動 Activity 的靜態方法。Java 中的實現如下: class LaunchActivity extends AppCompatActivity { public static final String TAG = LaunchActivity.class.getName(); public static void start(Context context) { context.startActivity(new Intent(context, LaunchActivity.class)); } } 在 Kotlin 下的實現如下: class LaunchActivity { companion object { val TAG: String = LaunchActivity::class.simpleName fun start(context: Context) { context.startActivity(Intent(context, LaunchActivity::class)) } } } Timber.v("Starting activity ${LaunchActivity.TAG}") LaunchActivity.start(context) 有了 companion object 後,就跟類多了一個單例的物件和方法一樣。 類委託 (37:58) 委託是一個大家都知道的設計模式,Kotlin 把委託視為很重要的語言特性。下面是一個在 Java 中典型的委託寫法: public class MyList<E> implements List<E> { private List<E> delegate; public MyList(List<E> delegate) { this.delegate = delegate; } // ... public E get(int location) { return delegate.get(location) } // ... } 我們有一個自己的 lists 實現,通過建構函式將一個 list 儲存起來,存在內部的成員變數裡,然後在呼叫相關方法的時候再委託給這個內部變數。下面是在 Kotlin 裡的實現: class MyList<E>(list: List<E>) : List<E> by list 用 by 關鍵字,我們實現了一個儲存 E 型別的 list,在呼叫 List 相關的方法時,會自動委託到 list 上。 譯者注:參考 Kotlin 官方文件瞭解更多。 宣告點變型(Declaration-Site Variance) (39:03) 這個可能是一個比較容易讓人迷惑的主題。首先,我們用一個協變陣列來開始我們的例子,下面的程式碼能夠很好的編譯: String[] strings = { "hello", "world" }; Object[] objects = strings; string 陣列可以正常的賦值給一個 object 陣列。但是下面的不行: List<String> strings = Arrays.asList("hello", "world"); List<Object> objects = strings; 你不能分配一個 string 型別的 list 給一個 object 型別的 list。因為 list 之間是沒有繼承關係的。如果你編譯這個程式碼,會得到一個型別不相容的錯誤。想要修復這個錯誤,我們得用到 Java 中的點變型(use-site variance)去宣告,所謂的點變型就是在宣告 list 可接受型別的時候,用extends 關鍵字給出引數型別的可接受類型範圍,比如類似如下的例子: 譯者注:點變型只是一個名字,不要太在意為什麼叫這個,簡單理解就是類似萬用字元原理,具體可以檢視這個維基頁面。 public interface List<E> extends Collection<E> { public boolean addAll(Collection<? extends E> collection); public E get(int location); } addAll 方法可以接受一個引數,引數型別為所有繼承自 E 的型別,這不是一個具體型別,而是一個類型範圍。每次呼叫 get 方法時,依然返回型別 E。在 Kotlin 中,你可以用 out 關鍵字來實現類似的功能: public interface List<out E> : Collection<E> { public fun get(index: Int): E } public interface MutableList<E> : List<E>, MutableCollection<E> { override fun addAll(c: Collection<E>): Boolean } 上面的一系列被稱為宣告點變型,即在宣告可接受引數的時候,就宣告為它是可變的。比如上面例子:我們宣告引數是可以允許所有繼承自 E 型別的,返回型別也為 E 的。 現在,我們有了可變和不可變型別的列表。可變性(variance) 其實很簡單,就是取決於我們在宣告的時候是動作。 譯者注:其實不論宣告點變型(Declaration-Site Variance) 還是 點變型(Use-site variance) 都是為了實現泛型的型別宣告,標註泛型型別可支援的範圍,釐清泛型型別上下繼承邊界。參考Generic Types。 操作符過載 (41:26) enum class Coin(val cents: Int) { PENNY(1), NICKEL(5), DIME(10), QUARTER(25), } class Purse(var amount: Float) { fun plusAssign(coin: Coin): Unit { amount += (coin.cents / 100f) } } var purse = Purse(1.50f) purse += Coin.QUARTER // 1.75 purse += Coin.DIME // 1.85 purse += Coin.PENNY // 1.86 上面的程式碼中,我們建立了一個硬幣列舉,每個硬幣列舉都代表一個特定數額的硬幣。我們有一個 Purse(錢包) 類, 它擁有一個 amount 成員變數,代表錢包裡現在有多少錢。我們建立了一個叫做 plusAssign 的函式,plusAssign 是一個保留關鍵字。這個函式會過載 += 操作符,也就是說當你在呼叫 += 符號的時候,就會呼叫這個函式。 隨後,建立一個 purse 例項,可以直接用 += 操作來實現給錢包裡放錢進去。 ============================================================ 建立bean類 class Person{ var name: String = "" var age: Int = 0 var college: String? = null } 宣告變數必須使用關鍵字var,而如果要建立一個只讀/只賦值一次的變數,則需要使用val代替它 在上述程式碼中,變數name和age不可為空,而?表明變數college可以為空 建立例項 var jake = Person() 注意,Kotlin沒有關鍵字new。 例項建立完成後,就可以像在Java中一樣為變數賦值了: 1.直接賦值 jake.name = "Jake Hill" jake.age = 24 jake.college = "Stephen's College" 2.通過建構函式賦值 class Person(var name: String, var age: Int, var college: String?) { } 由於建構函式中沒有其它操作,所以花括號也可以省略 class Person(var name: String, var age: Int, var college: String?) var jake = Person("Jake Hill", 24, "Stephen's College") 上述程式碼中的建構函式是類頭的一部分,稱為主建構函式。在Kotlin中,還可以使用constructor關鍵字建立輔助建構函式,例如,下面的程式碼增加了一個輔助建構函式初始化變數email: class Person(var name: String, var age: Int, var college: String?) { var email: String = "" constructor(name:String, age:Int, college: String?, email: String) : this(name, age, college) { this.email = email } } Kotlin允許建立派生類,但要遵循如下規則: ● 必須使用:代替Java中的extends關鍵字 ● 基類頭必須有open註解 ● 基類必須有一個帶引數的建構函式,派生類要在它自己的頭中初始化那些引數 比如下面的程式碼建立了一個名為Empoyee的派生類: open class Person(var name: String, var age: Int, var college: String?) { ... } class Employee(name: String, age: Int, college: String?, var company: String) : Person(name, age, college) { } ====================================================== 函式與擴充套件 有派生就有過載。與類的派生一樣,允許過載的方法要有open註解,而在派生類中過載時要使用override註解。例如,下面是在Employee類中過載Person類的isEligibleToVote方法的程式碼: override fun isEligibleToVote(): Boolean { return true } 除了改變類的已有行為,Kotlin還允許開發者在不修改類的原始定義的情況下實現對類的擴充套件,如下面的程式碼為Person類增加了一個名為isTeenager的擴充套件: fun Person.isTeenager(): Boolean { return age in 13..19 } 上面提到的函式都與Java中的函式類似,但Kotlin還支援其它型別的函式。如果一個函式返回單個表示式的值,那麼可以使用=來定義函式。下面是一個建立單表示式函式的例子: fun isOctogenarian(): Boolean = age in 80 .. 89 Kotlin還支援高階函式和Lambda表示式。例如,lambda表示式{x,y->x+y}可以像下面這樣給一個變數賦值: val sumLambda: (Int, Int) -> Int = {x,y -> x+y} 而下面的高階函式將上述表示式作為一個引數,並將表示式的計算結果翻倍: fun doubleTheResult(x:Int, y:Int, f:(Int, Int)->Int): Int { return f(x,y) * 2 } 該函式可以使用下面的其中一種方式呼叫: val result1 = doubleTheResult(3, 4, sumLambda) 或 val result2 = doubleTheResult(3, 4, {x,y -> x+y}) ================================================================ 範圍表示式 在Kotlin中,範圍表示式用的比較多。範圍建立只需要..操作符,例如: val r1 = 1..5 //該範圍包含數值1,2,3,4,5 如果建立一個降序範圍,則需要使用downTo函式,例如: val r2 = 5 downTo 1 //該範圍包含數值5,4,3,2,1 如果步長不是1,則需要使用step函式,例如: val r3 = 5 downTo 1 step 2 //該範圍包含數值5,3,1 =============================================================== 條件結構 在Kotlin中,if是一個表示式,根據條件是否滿足返回不同的值,例如,下面的程式碼將isEligibleToVote設定為“Yes” var age = 20 val isEligibleToVote = if(age > 18) "Yes" else "No" when表示式相當於Java的switch,但功能更強大,例如,下面的程式碼將typeOfPerson設定為“Teenager”: val age = 17 val typeOfPerson = when(age){ 0 -> "New born" in 1..12 -> "Child" in 13..19 -> "Teenager" else -> "Adult" } ============================================================== 迴圈結構 Kotlin使用for..in遍歷陣列、集合及其它提供了迭代器的資料結構,語法同Java幾乎完全相同,只是用in操作符取代了:操作符,例如,下面的程式碼將遍歷一個String物件陣列: val names = arrayOf("Jake", "Jill", "Ashley", "Bill") for (name in names) { println(name) } whiledo..while迴圈的語法與Java完全相同。 ========================================================================== 列舉 enum 類。 類語法 (5:19) 我們來看看類。類的定義要通過 class 關鍵字,跟 Java 裡的一樣,關鍵字後是類名。Kotlin 有一個主建構函式,我們可以直接將建構函式引數列表寫在類的宣告處,還可以直接用 var 或者 val關鍵字將引數宣告為成員變數(又稱:類屬性),如下: class Person(var name: String) 繼續之前的例子,有了主建構函式以後,我們就不再需要成員變數賦值語句了。在 Kotlin 裡建立例項的時候,不必使用 new 關鍵字。你只需要指明建立的型別名就可以建立例項了。 class Person(var name: String) fun main(args: Array<String>) { val person = Person("Michael") println("Hello, $name!") } 很容易發現,字串插值實際上是錯誤的,因為 name 指向的是一個不存在的變量了。我們可以用剛才提到的 字串插值表示式 ,即用 $ 符號和大括號包裹想要插入的變數,來修復這個問題: class Person(var name: String) fun main(args: Array<String>) { val person = Person("Michael") println("Hello, ${person.name}!") } 下面是 enum 類。列舉跟 Java 裡的列舉很像。定義一個列舉的方法如下: enum class Language(val greeting: String) { EN("Hello"), ES("Hola"), FR("Bonjour") } 我們來給 Person 類增加一個叫 lang 的屬性,代表一個人的所說的語言。 class Person(var name: String, var lang: Language = Language.EN) Kotlin 支援引數預設值,如上:language 的預設值就是 Language.EN,這樣就可以在建立例項的時候忽略這個引數,除非你要改變 language 的屬性值。我們來把這個例子變得更面向物件一些,給 person 增加一個打招呼的方法,簡單地輸出特定語言打招呼的方法還有人名: enum class Language(val greeting: String) { EN("Hello"), ES("Hola"), FR("Bonjour") } class Person(var name: String, var lang: Language = Language.EN) { fun greet() = println("${lang.greeting}, $name!") } fun main(args: Array<String>) { val person = Person("Michael") person.greet() } 現在在 main 函式裡呼叫 person.greet() 方法,看看是不是很酷?! 集合和迭代 (11:32) val people = listOf( Person("Michael"), Person("Miguel", Language.SP), Person("Michelle", Language.FR) ) 我們可以用標準庫函式 listOf 方法建立一個 person 列表。遍歷這些 person 可以用 for-in 關鍵字: for (person in people) { person.greet() } 隨後,我們可以在每次遍歷的時候執行 person.greet() 方法,甚至可以更簡單,直接呼叫 people 集合的擴充套件方法 forEach,傳入一個 lambda 表示式,在表示式裡用 it 代表每次遍歷到的person 物件,然後呼叫它們的 greet 方法。 people.forEach { it.greet() } 我們來建立兩個新的類,每個都傳入一個預設的語系。我們可以不再像剛才那樣重複宣告,可以直接用繼承的方法來實現。下面是一個擴充套件版本的 Hello World。展示了很多 Kotlin 的特性: enum class Language(val greeting: String) { EN("Hello"), ES("Hola"), FR("Bonjour") } open class Person(var name: String, var lang: Language = Language.EN) { fun greet() = println("${lang.greeting}, $name!") } class Hispanophone(name: String) : Person(name, Language.ES) class Francophone(name: String) : Person(name, Language.FR) fun main(args: Array<String>) { listOf( Person("Michael"), Hispanophone("Miguel"), Francophone("Michelle") ).forEach { it.greet() } } ===============================================================================

安卓開發交流群 : 595856941