kotlin的基本用法詳解
阿新 • • 發佈:2019-01-28
基本用法
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中我們都很清楚基本型別有 byte、short、int、long、double、float、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的值為true或false
● 5.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,就將其打印出來。我們檢測的範圍是 1 到 10。
if (1 <= i && i <= 10) {
println(i)
}
其實我們可以用 intRange 函式來完成這個操作。我們傳入 1 和 10,然後呼叫 contains 函式來判斷是否在這個範圍裡。我們打印出 i 即可。
if (IntRange(1, 10).contains(i)) {
println(i)
}
這個還可以用擴充套件函式來實現,1.rangeTo 建立了一個 1 到 10 的 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. }
======================================================================
8、for迴圈和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 即可!不論你在什麼域裡,你都可以像單例一樣呼叫這個 object。
object 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)
}
while和do..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