Kotlin 語言高級安卓開發入門


過去一年,使用 Kotlin 來為安卓開發的人越來越多。即使那些現在還沒有使用這個語言的開發者,也會對這個語言的精髓產生共鳴,它給現在 Java 開發增加了簡單並且強大的范式。Jake Wharton 在他的 Øredev 的討論中,提到了 Kotlin 是如何通過提升安卓開發的語言特性和設計模式來解決這些嚴重的問題,通過這些方法你可以清除那些無用的 API 還有無效代碼。充分利用擴展特性來解決你的開發中的模板性代碼的問題!

 
為什么要推廣這個語言?
 
好吧,大伙。歡迎來到這里。我們今天的主題是使用 Kotlin 語言的高級安卓開發 – 推廣我們開發應用的語言。首先呢,我們需要弄清楚我們為什么要在安卓開發中更進一步?為什么我們需要一些新的東西來推進安卓開發?
 
許多人都會提到的一個經典的原因就是我們現在被 Java 6-ish 困住了,Java 7 也差不多是同樣的狀況。這被稱作是一片荒地。
 
javax.time 是 Java 8 才引進的新庫, JSR310。這個庫我們在安卓上沒有,所以我們被老的日期和日歷的 API 困住了,它們都非常容易出錯。
 
Java 8 的另外一個功能 streams,安卓也沒有。我們還缺少其他的語言特性,比如 lambdas,method references 和 不引用 outer class 的 anonymous classes。最后,我們沒有 try-with-resources。Try-with-resources 不在 Java 7 里面,而安卓在 API 19 中大部分都是 Java 7, 在 API 20 中又增加了點,但不是全部。所以只有當你的定義最小的 SDK 的時候,你才能用這些相關的功能。
 
Java 6 不是個問題 
 
盡管上面4個問題都看起來十分令人頭疼,但是事實上它們不是。關於 javax.time,為 Java 8 SDK 編寫的大部分代碼大部分都能夠以 ThreeTenBP 的形式向后兼容 Java 6,所以你可以這樣使用。
 
關於 streams,Java 6 上也有一個向后兼容的移植或者一個常用的 Java 庫,RxJava 實現了同樣的概念,只不過用了一種略微不同的有爭議性的更強大的方式。
 
也有一個工具叫做 Retrolambda,它事實上也可以運行 Java 8 bytecode 並且向后兼容了 lambdas 和 method references 到 Java 6。
 
最后,如我所說,如果你的最小的 SDK 是 19 的話,你可以使用 try-with-resources,而且 Retrolambda 也會允許你這樣做。
 
這些關於 Java 6 是個問題的爭論都有工具去解決它們。當然,如果我們能使用 Java 8 是最好的,但是這些替代方案都是經過實戰測試而且能夠很好地工作的方案。所以上面的理由就不太算數了。無論如何,有兩個方面我想重點討論一下,因為這兩個方面沒有類似的解決方案。
 
Java 語言的限制和問題
 
Java 語言的一些限制和所帶來的一些問題,這些問題 Java 語言本身是不可避免的。例如,我們不能給不是我們自己寫的 types、classes 或者 interfaces 增加新的方法。長時間以來,我們都會采用 util 類,雜亂無章地堆砌着我們代碼或者或者揉在同一個 util package 里面。如果這是解決方案的話,它肯定不理想。
 
Java 語言的類型系統都有 null 的問題,在 android 上更為明顯。因為它沒有對可能是或不是 null 的類型完成 first-class representation。所以,被稱作 “billion dollar mistake” 的 null 指針異常最后會毀了你的應用。
 
接下來, Java 肯定不是最簡潔的語言。這件事本身不是件壞事,但是事實上存在太多的常見的冗余。這會帶來潛在的錯誤和缺陷。在這之前,我們還要處理安卓 API 帶來的問題。
 
Android API 的設計問題
 
安卓是一個龐大的繼承系統;他們為他們的繼承系統而感到自豪,對他們來說這個系統工作的也非常的正常,但是這個系統傾向於把問題推向應用開發者。而且,空引用的問題也回來了,這個問題在安卓系統中十分明顯,因為他們想讓系統更加有效些。null 被用在許多地方來代表值的缺失,而不是封裝成更高級的類型,比如一個類型或者可選項。
 
同時,回到語言的冗余,安卓的 API 有着自己許多的范式。這也許是因為性能的原因。設計者們最后寫出的這些 APIs 需要你,開發者,來做許多的事情來提高效率而不是用其他的方法抽象它們。
 
所以說 Java 語言和安卓 API 是驅動我們轉向類似 Kotlin 語言的兩個重要的因素。這不是說 Java 6 沒有問題,因為 Kotlin 解決了 Java 6 的許多問題,但是我不認為那是 Kotlin 作為一個替代方案的充分原因,因為其他的方案也可以帶來同樣的好處。
 
Kotlin 入門 
 
Kotlin 是公司 JetBrains 研發的語言。市面上為各種語言開發的 IDE 很多,但是 Intelli J 平台是 Android Studio 的基礎。在他們的網站上,他們這樣描述 Kotlin :
 
為 JVM、Android 和瀏覽器而生的靜態編程語言。
它的目標是 JVM、安卓和 Java 6 字節流。他們想在他們的語言里增加這些特性,而且持續支持 Java 6、JVM 和安卓系統市場。他們特別關注和 Java 的相互調用,這點接下來會討論。
 
Kotlin 語法速成
 
網站上有許多的很棒的指導來學習語法,但是我還是會很快地介紹一下,然后再解釋為什么這些語法有利於安卓開發。我們以一個這樣的方法定義的語法開始。
 

[代碼]csharp代碼:

?
1
2
3
fun sum(a: Int, b: Int): Int {
   return a + b
}
我們有一個 “fun” 的定義,這代表着函數。函數名和第一個要注意的事情是這和 Java 有明顯的不同。參數名的順序和參數的類型保留了下來 – 名字后面跟着類型。返回值類型在函數末尾聲明。沒有分號。
 
另外一個有意思的事情是這個函數還可以有單行描述,我們事實上可以不用大括號和 return,定義函數和表達式一樣。
 

[代碼]csharp代碼:

?
1
fun sum(a: Int, b: Int) = a + b
我們接下來還會看到更簡潔的語法。這里有另外一個例子,這個看起來像一個 main 函數,如果你寫一個普通的 Java 應用的話:

[代碼]csharp代碼:

?
1
2
3
fun main(args: Array< string >) {
   println( "Args: $args" )
}</ string >
數組的語法不太一樣。但是處理得十分自然。 編譯后的字節碼會使用一個字符串的數組,但是在你的代碼里卻把它處理地像一個普通的數組。它也支持字符串的插入;我們可以寫一個串,然后引用它其中的變量,並且可以自動的替換其中的變量。
 
最后,看看變量:

[代碼]csharp代碼:

?
1
2
val name = "Jake"
val people: List< string > = ArrayList()</ string >
這里我用一個叫做 “name” 的變量給一個字符串命名,而且這里沒有類型定義。語言會自動解釋類型,因為它只可能是串。它有 “val” 的前綴而 “Val” 是它的值,並且是個不可以修改的值。如果我們想修改它,我們就需要用 “var” 作為前綴定義變量。
 
這個 : List<String> 是一個看起來像在 field 上的類型,它接在名字后面,像一個方法。最后,當我們調用構造函數的時候,我們不需要使用 “new” 關鍵字。其他的語法都是一樣的,就是不要 “new”。
 
Kotlin 語言特性 
 
讓我們看看那些語言本身的特性,看看他們是如何幫助我們構建安卓應用的。我指出過這些 util 類都是反設計模式的,而且它們會在你的應用里越來越不受控制……
 
函數擴展 
 
Kotlin 有擴展函數的概念。這不是 Kotlin 語言獨有的,但是和其他語言里面我們看到的擴展又不太一樣。如果我們在純 Java 語言的環境下添加一個 date 的方法,我們需要寫一個 utils 類或者 dates 類,然后增加一個靜態方法。它接收一個實例,然后做些事情,可能會返回一個值。
 

[代碼]java代碼:

?
1
2
3
4
5
static boolean isTuesday(Date date) {
   return date.getDay() == 2 ;
}
 
boolean tuesday = DateUtils.isTuesday(date);
這里我增加一個十分有用的 “isTuesday” 函數給我們的 date utils,然后我們用傳統的靜態方法調用它。在我展現 Kotlin 語法前,我想和 C# 做個比較。這是在 C# 中我們需要在 date 類中添加一個函數的實現, DateTime:

[代碼]java代碼:

?
1
2
3
4
static boolean IsTuesday( this DateTime date)
{
   return date.DayOfWeek == DayOfWeek.Tuesday;
}
這里我們得到 date 的實例,然后調用這個方法,在任何 .NET 環境下這都能行得通,只要你在某處定義了該擴展方法,你能在你的整個項目中都能引用到 DateTime。我接下來會解釋一個有意思的語言特征。這是 Kotlin 定義的方法:

[代碼]csharp代碼:

?
1
2
3
4
5
fun Date.isTuesday(): Boolean {
   return getDay() == 2
}
 
val tuesday = date.isTuesday();
在 Kotlin 中,我們用我們想增加的函數的方法的類型來給原有的函數名增加了一個前綴。我們現在調用 Date.IsTuesday 而不是 isTuesday。然后你能在最后得到返回值。我們最后能調用 “getDay” 並且這個擴展的方法能夠被調到,盡管我們沒有使用實例來調用它。我們的調用方式和該類原來就有有這個方法時調用的方式一樣。我們也能夠在 Date 上調用其他的方法。
 
Kotlin 的一個非常好的功能是,它會自動地轉換有 getters 和 setters 綜合屬性的類型。所以我能夠替換 getDay() 為 day,因為這個 day 的屬性是存在的。它看起來像一個 field,但是實際上是個 property – getter 和 setter 的概念融合在了一起。
 
前面我指出的單行函數表達式會使得語法變得更簡潔,所以我們可以把上面的代碼修改成下面的樣子,並且隱藏返回值:

[代碼]csharp代碼:

?
1
fun Date.isTuesday() = day == 2
現在我們有一個非常漂亮的單行實現的並且在 date 上使用的擴展方法了。
 
不像 C#,如果我們不在同一個 package 里面的話,擴展函數需要顯示引用。如果不是同一個文件里,我們需要非常清楚地描述這個函數從何而來: import com.example.utils.isTuesday
 
這和 Java 的靜態方法 import 非常類似。我們需要顯示聲明我們調用的函數,這樣函數的來源就不會模糊。因為當我看到這段代碼時,作為一個 Java 開發者,我知道 date 沒有 “isTuesday” 的函數,但是顯式的 import 告訴我它來自於公共的某個 util package。而在 C# 中,我們不知道這個擴展函數從何而來。它可能來自一個庫,你的源代碼,或者其他的地方,你無法靜態地知道,除非你到 IDE 里面找到它的定義。
 
當然,你可以 command B 然后進入這個函數,這看起來像是 date 類型的一個方法。這和我們在 Java 里面編寫它,然后產生的二進制代碼一模一樣。而且,因為它關注 inter-op,你可以從 Java 側使用一個自動生成類來調用它。
 
類型系統中的 Null 
 
我提過 null 會是個問題。Kotlin 事實上在它的類型系統里重新表述了 null。對於 get string 可能返回 null 的函數來說,我會返回 String? 來表述這可能是個 null 值。然后在 get string 函數中,我使用 double exclamation mark 語法來直接調用這個 null。這基本上是說,“我知道這可能是個 null,所以把它變成一個普通字符串。” 當它真是 null 的時候,它會發出一個檢查的信號,然后拋出異常。
 
但是在消費者的代碼里面,代碼往往是直接調用該函數,類型系統會生成一個帶有問號的字符串,並且傳遞到調用者的代碼處。這意味着如果你不首先做 null 檢查或者提前的默認處理機制,你就永遠不能解引用。這樣,它會最終解決消費者代碼中導致 null 指針異常的問題。
 
函數表達式入門 
 
另外,函數表達式也被稱作 lambdas 或者 closures。這里有一個最簡單的函數表達式: { it.toString() }。它是一段代碼在 “it” 變量上調用了 two-string 函數。“it” 是個 built-in 的名字。當你在寫這些函數表達式的時候,如果你只有一個參數傳入這段代碼,你可以用 “it” 引用,這只是一個你不需要定義參數的方法。
 
但是當你需要定義參數的時候,或者不止一個參數要定義的時候,語法就是這樣的: { x, y -> x + y }。我們可以創建一段代碼,一個函數表達式,輸入兩個參數,然后把它們相加。如果我們願意,我們可以顯示定義類型。
 

[代碼]csharp代碼:

?
1
2
3
4
5
{ x, y -> x + y }
 
val sum: (Int, Int) -> Int = { x, y -> x + y }
 
val sum = { x: Int, y: Int -> x + y }
然后,在 fields 上存儲它們。現在你可以看到在前二個例子中,類型是在 field 本上上定義的。這意味着函數表達式不需要任何類型信息。然后往下一個,類型信息在函數表達式里面自己包含了,所以我們不需要在變量定義時加入類型信息。
 
在最后一個例子中,返回值是推斷出來的。兩個整型相加,輸出只能是整型。所以你不需要顯示定義它們。
 
Higher-order 函數 
 
這是個新奇的術語,它指的是函數可以接收函數,或者函數可以返回函數。這里是個例子

[代碼]csharp代碼:

?
1
2
3
4
5
6
func apply(one: Int, two: Int, func: (Int, Int) -> Int): Int {
   return func(one, two)
}
 
val sum = apply(1, 2, { x, y -> x + y })
val difference = apply(1, 2, { x, y -> x - y })
這里我們定義一個函數,接收兩個整型作為參數。然后第三個參數是一個函數。這和我們之前看到的語法一樣,那里我們定義了一個函數接收兩個整型並返回一個整型。
 
然后,在函數體內部,我們調用了該函數,並且傳入了兩個參數。這就是我們如何使用它來計算和或者其他的東西的方法。我們把這段代碼應用到了這兩個數字上。回到之前所說的地方,我們說這段代碼知道如何相加或者相減,而且我們把它運用到我們傳入這個方法的數據上。然后這段代碼在自己合適的上下文環境中自動運行。
 
Kotlin 給你提供了一個手動的方式來調整這段代碼成為更好的語法表達。如果函數的最后一個參數是個表達式,你一直都不需要使用括號,你可以只用在所有參數初始化后添加一個。

[代碼]csharp代碼:

?
1
2
val sum = apply(1, 2) { x, y -> x + y }
val difference = apply(1, 2) { x, y -> x - y }
這項技術允許你創建非常漂亮的 DSL 和 API。你可以像 Int 的擴展一樣來編寫它們。
 
包含 higher-order 函數的應用 
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
fun <t> List<t>.filter(predicate: (T) -> Boolean): List<t> {
   val newList = ArrayList<t>()
   for (item in this ) {
     if (predicate(item)) {
       newList.add(item)
     }
   }
   return newList
}
 
val names = listOf( "Jake" , "Jesse" , "Matt" , "Alec" )
val jakes = names.filter { it == "Jake" }</t></t></t></t>
一個常見的操作是你有一個事情的列表,然后你需要基於一些條件來過濾它們。因為我們有 extension methods 和 higher-order functions,我們可以在列表上寫一個這樣的函數。這和你期望的實現一樣,這個函數比較每個列表中的元素然后返回正確的結果。
 
現在如果你有一個包含數據的列表,只需用一行代碼,如此簡潔的函數就能過濾出我們想要的數據。謝天謝地,這個功能實際上已經在 Kotlin 標准庫里面包含了,這樣我們就不用擴展列表類了。
 
大部分 Kotlin 的庫都已經實現了現有的 Java 類型中類似的高級函數來,這樣可以統一在這些類型上的操作。
 
看看這個在 Kotlin 標准庫里面不存在的應用:

[代碼]csharp代碼:

?
1
2
3
4
5
fun Any. lock (func: () -> Unit) {
   synchronized ( this ) {
     func()
   }
}
除了同步阻塞代碼塊,我們可以用一個表達式來實現同步阻塞。 “Any” 是 Kotlin 的對象版本。所有的類型都是 “Any” 的子類型。我們可以對它增加一個方法,該方法輸入是一個函數,然后執行這個函數的代碼,這段代碼會同步該實例。如果我們需要同樣的鎖,或者一些對象需要被鎖住,我們可以把這段代碼放在函數內部的鎖起來。然后我們把它傳給每個實例的這個方法中。這個方法簡化了調用者的代碼,而且清晰很多。
 
簡單清晰的鎖
 
另一個十分酷的應用存在於我們常用的鎖的情景中,我們常常忘記在一些操作之前完成鎖的操作。我們可以寫這樣的類,這個類只允許你訪問你需要用鎖才能操作的資源。

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
data class Lock<t>( private val obj: T) {
   public fun acquire(func: (T) -> Unit) {
     synchronized (obj) {
       func(obj)
     }
   }
}
 
val readerLock = Lock(JsonReader(stream))
 
// Later
readerLock.acquire {
   println(it.readString())
}</t>
在這個例子里面,我們創建了依賴流的 JsonReader。設想不論什么原因我們需要多線程同時訪問它,這樣我們就必須使用鎖,鎖會幫助我們管理同步的問題。
 
然后在后面的代碼中,我們調用了這個 acquire 的方法,它會在這個 JsonReader 的實例上實現同步,然后把它傳給我們提供的函數。所以在這種情況下,我們會在這個 JsonReader 里面再次析構,於是我們需要使用鎖。但現在我們的代碼根本沒有處理鎖的問題。我們也沒有顯式的同步,也沒有為 JsonReader 創建鎖。可是訪問 JsonReader 不使用鎖是不可能的。
 
避免 Kotlin 帶來的泄露
 
前面,我提到過關於 data 的代碼,定義了一個這樣的值:
 

[代碼]csharp代碼:

?
1
val notEmpty: (String) -> Boolean { !it.isEmpty() }
Kotlin 實現函數表達式的方法和在 Java 里面使用類的方法是一樣的。好處是 Kotlin 並沒有創建對於外部范圍的引用,因為沒有這樣的類。這樣避免了可能的上下文泄漏。
 
我們需要關心的就是傳入的數據。它會導致創建一個靜態的單例實例而且沒有引用。用這些函數表達式根本不可能產生上下文泄漏。但是在最上層,我們兩個是一模一樣的。
 
擴展函數表達式的例子
 
擴展函數 – 給一個類型加入函數但是不修改原來的類型。
 
函數表達式 – 未定義的函數體被用作表達式(i.e.,date)
 
Higher-Order 函數 – 一個參數是函數或者返回是函數的函數。
 
擴展函數表達式是上述三個概念的綜合體,這是個強大的而且可以創建清晰的 API 的方法。為了說明這點,我將使用一個 databases 的 API 做為例子,把它改造成一個更加整潔的、沒有無效代碼的 API。
 

[代碼]csharp代碼:

?
1
2
3
4
5
6
7
db.beginTransaction();
try {
   db.delete( "users" , "first_name = ?" , new String[] { "Jake" });
   db.setTransactionSuccessful();
} finally {
   db.endTransaction();
}
如果你想在一個 transaction 里面執行一個 statement 的話,這是你必須寫的六行代碼。我們開始一個 transaction,我們把它放在 “try finally” 里面,然后我們標記這個 transaction 為成功。如果它拋出或者不在 “finally” 里面拋出異常,我們需要結束 transaction.
這是一個容易帶來 bug 的代碼,容易健忘的代碼和應該重構的代碼。任何事情都有可能出現錯誤,在這里你不小心交換了兩個事情,然后突然,你就會有一個很難找到原因的 bug。 或者它會在運行時 crash。
擴展函數表達式允許我們解決這個問題。我們現在能給 database 自己增加一個方法, 這將給現有的代碼構建一個防護牆。
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
fun SQLiteDatabase.inTransaction(func: (SQLiteDatabase) -> Unit) {
   beginTransaction()
   try {
     func( this )
     setTransactionSuccessful()
   } finally {
     endTransaction()
   }
}
 
db.inTransaction {
   it.db.delete( "users" , "first_name = ?" , arrayOf( "Jake" ))
}
開始一個 transaction,調用 “try” 代碼段,然后設置 transaction 為成功,最后,結束它。我們接收一個函數作為參數,在使用這個小技巧后,我們在 “try” 代碼段里面執行該函數。在我們的消費者代碼里,我們會調用這個我們稱作 inTransaction 的方法,在這個方法里面我們寫的代碼都會在一個 transaction 里面執行。你沒有別的辦法來錯誤地使用它了。
 
渠道化你的內部 SQLiteDatabase 
 
一個有趣的事情是,在這樣的實現方法下,你的代碼里面還是會需要 database 的引用。這也是可能會出錯的地方。如果你有兩個數據庫,你可能會在 transaction 里面引用了那個錯誤的數據庫。
 
我們有多種方法來解決這個問題。一個方法就是我們可以讓函數的參數包含需要發生 transaction 的數據庫的引用。我們傳了 this 給函數,然后我們就不需要調用 “db”,而是使用 “it” 了,它是傳給表達式的第一個參數。
 
這個方法還是不太好。我們阻止了潛在問題的發生,但是現在我們每次訪問 database 的時候都需要調用 “it”。這里有個有意思的事情是輸入參數是個函數。我們可以把這個函數改寫成 “SQLiteDatabase” 的一個擴展函數。
 
這將會使你有些迷惑,因為它是個瘋狂的強大的概念。通過把 func(this) 替換成 this.func(),傳入的函數參數變成了該對象的擴展函數,而且這個擴展函數已經存在 “SQLiteDatabase” 里了。因為我們並不是真正需要 this,所以我們拋棄了它。
 
這個函數會像在 SQLiteDatabase 里面定義的那樣執行。如果我們調用刪除方法,我們不需要用任何東西來證明自己有資格調用,因為我們就是個 SQLiteDatabase。現在我們可以不需要 it 了,所以每一個你放在這個代碼段里面的表達式都會看起來像 SQLiteDatabase 內部的一個私有方法一樣。你不需要證明自己是正確的,因為它總是能在正確的對象上執行自己。
 
這太棒了,它會把你的代碼改編成:

[代碼]csharp代碼:

?
1
2
3
4
db.inTransaction {
   it.db.delete( "users" , "first_name = ?" , arrayOf( "Jake" ))
}<font face= "Tahoma, Arial, Helvetica, snas-serif" ><span style= "font-size: 14px; white-space: normal;" >
</span></font>
避免額外的垃圾回收
 
這仍然是安卓的一個大問題。等效的 Java 代碼中,我們創建了一個函數的新的實例,然后把它傳給了我們產生的靜態方法。這非常不幸,因為在我們順序執行代碼之前,我們並沒有真正地分配內存。
 
如果我們用 Java 來實現這個,我們需要在每次使用這些函數表達式的時候都分配一些極小的函數對象。這不好,因為這會觸發許多的垃圾回收。
 
謝天謝地,我們有辦法在 Kotlin 里面來避免這個問題。我們這里的函數僅僅是個定義好的函數表達式。它使用函數表達式作為入參,這會是個問題。那個函數表達式需要轉成一個匿名類。

[代碼]csharp代碼:

?
1
inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { ... }
我們可以把它變成一個 in-line 的函數,這樣我們就告訴了 Kotlin 編譯器不要把它作為一個靜態函數調用,我需要編譯器僅僅把我的函數代碼替換到需要調用的地方。雖然這樣會產生很多的 bytecode,但是這樣產生的 Java 類文件會和容易出錯的 Java 代碼編譯出來的類文件一樣的。
 
經驗法則:函數表達式加上 in-line 函數和同樣實現的 Java 代碼一模一樣。現在我們就能清理我們想清理的任何 API 了,找到你安卓里面最差的 API,這樣的 API 肯定到處都是。這其中有很多是基於 transaction 的,所以我們可以為 fragments 或者為 shared preferences 使用同樣的模式。這是一個增加功能的好方法,而且不會對自動產生的代碼帶來額外開銷。
 
JetBrains 提供的 Anko 
 
JetBrains 的同事把這個思路用到了極致,並且創建了一個庫叫做 Anko。基本想法是 XML 描述性非常好,而且很適合定義 UI,因為它是分級的。當你創建 UI 的時候,它們也是具備分層特性的。
 
在這些函數表達式的幫助下,我們可以用同樣分層式方式來編寫代碼。當然我們還能夠引用其他所有的方法,其他的重構工具,和其他的 Java 代碼的靜態分析工具來展示 UI。
 

[代碼]csharp代碼:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
verticalLayout {
   padding = dip(30)
   editText {
     hint = "Name"
     textSize = 24f
   }
   editText {
     hint = "Password"
     textSize = 24f
   }
   button( "Login" ) {
     textSize = 26f
   }
}
這僅僅是擴展函數表達式的一個發展分支。它們會創建這些類的實例,把它們加到它們的父親那里,設置合適的屬性。除了我們在 XML 里面使用的概念以外,比如 layout,你可以調用一個函數來返回這些 layout 和把這些東西組織在一起。
 
這一定是個有趣的概念。但不一定適合每一個人。它們也有定制的預覽插件。使用 XML 的一個好處就是你可以預渲染視圖,然后看到它在設備上的樣子。他們也寫了一個為 Java 代碼的工具,這個工具可以解析 Kotlin 代碼並且完成渲染。
 
這也是個 XML 也會令人煩惱的例子。Java 和 XML 這兩個分離的系統,會帶來一些一般的麻煩。而這個方法會把這兩個分離的系統統一到一個 Koltin 的源文件里面。這會導致性能的提升,因為你減少了 XML 解析的開銷,也會減少尋找 XML 中定義的類而發生的反射的開銷。
 
所以雖然不是每個人都喜歡這個解決方法,但是這確是個解決他們碰到問題的新的方案。
 
結論 
 
我想給你們介紹現在解決安卓系統中的問題過程中的最有用的一些概念。Koltin 語言還有許多其他的一般性的改進,但那都是為了 Java 語言的。
 
這就是今天我想討論的讓你意識到的一些安卓系統開發的問題和可能解決它們的具體途徑。Kotlin 網站 有着非常多的好的資源。那有一個交互性編輯器,使用它你可以在你的瀏覽器里面創建和運行 Kotlin 代碼。
 
在同樣的編輯器里面,它們也有一系列的交互性教程來幫助你一步步學習語法。
 
語言還處在 1.0 beta 的狀態,所以對於那些因為這個原因而持觀望態度的人,你們很快就能加入了。我鼓勵你們都試試 Kotlin 語言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM