Kotlin優勢
- 空安全 :在編譯時期處理各種null情況,避免執行時異常。
- 函數式支持:它使用了很多函數式編程概念。
- 擴展函數:可以給任何類添加擴展函數。
- 高度互操作性:Kotlin和Java兩個語言之間可以互操作,兩種語言互相調用,混合編程。
構造函數
在 Kotlin 中的一個類可以有一個主構造函數和一個或多個次級構造函數。主構造函數是類頭的一部分:它跟在類名后。
//firstName可以在initializer blocks中使用
class Person constructor(firstName: String) {
}
如果主構造函數沒有任何注解或者可見性修飾符,可以省略這個 constructor
關鍵字。
class Person(firstName: String) {
}
主構造函數不能包含任何的代碼。
初始化的代碼可以放到以 init 關鍵字作為前綴的初始化塊(initializer blocks)中:
class Person(name: String) {
init {
CLog.d("Person initialized with name ${name}")
}
}
注意,主構造的參數可以在初始化塊中使用。它們也可以在類體內中聲明屬性時使用:
class Person(name: String) {
val customerKey = name.toUpperCase()
}
事實上,聲明屬性以及從主構造函數初始化屬性,Kotlin 有簡潔的語法,與普通屬性一樣,主構造函數中聲明的屬性可以是可變的(var)或只讀的(val):
class Person(val firstName: String, val lastName: String, var age: Int) {
// ……
}
如果構造函數有注解或可見性修飾符,這個 constructor 關鍵字是必需的,并且這些修飾符在它前面:
class Person private constructor(name: String) {
//......
}
次級構造函數
類也可以聲明前綴有 constructor的次構造函數。
如果類有一個主構造函數,每個次構造函數需要委托給主構造函數, 可以直接委托或者通過別的次構造函數間接委托。委托到同一個類的另一個構造函數用 this 關鍵字即可。
如果你沒有委托主構造函數,系統會提示:Primary constructor call expected
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一個非抽象類沒有聲明任何(主或次)構造函數,它會有一個生成的不帶參數的主構造函數。構造函數的可見性是 public。如果你不希望你的類有一個公有構造函數,你需要聲明一個帶有非默認可見的空的主構造函數:
class DontCreateMe private constructor () {
}
繼承
在 Kotlin 中所有類都有一個共同的超類 Any,它是沒有繼承父類的類的默認超類:
class Example // 從 Any 隱式繼承
默認每個類在創建的時候都是final的,如果沒有添加open
關鍵字,你在繼承的時候系統會提示This type is final, so it cannot be inherited from
。
要聲明一個顯式的父類,我們把類型放到類頭的冒號之后:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如果該父類有一個主構造函數,則子類可以(并且必須) 用父類型的)主構造函數參數就地初始化。如果沒有執行這一步,系統會提示This type has a constructor , and thus must be initailized here
。
如果類沒有主構造函數,那么每個次構造函數必須使用 super 關鍵字初始化其父類型,或委托給另一個構造函數做到這一點。 注意,在這種情況下,不同的次構造函數可以調用父類型的不同的構造函數:
class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
}
如果你不執行這一步,系統會提示你Explicit "this" or ''super" call is required. There is no constructor in superclass that can be called without arguments
類上的open 標注與 Java 中 final 相反,它允許其他類從這個類繼承。
默認情況下,在 Kotlin 中所有的類都是 final。
要么為繼承而設計,并提供文檔說明,要么就禁止繼承。
伴生對象
在 Kotlin 中類沒有static的方法和變量,所以需要使用Companion Object來聲明類似static的方法和變量。
其實這些變量并不是真正的static變量,而是一個伴生對象,這個伴生對象位于類中定義的一個叫做Companion的內部類中。
類內部的對象聲明可以用 companion 關鍵字標記:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
該伴生對象的成員可通過只使用類名作為限定符來調用:
val instance = MyClass.create()
在Java中如何調用Companion Object的屬性和方法呢?
MyClass.INSTANCE.create()
當然,在 JVM 平臺,如果使用 @JvmStatic
@JvmField
注解,你可以將伴生對象的成員生成為真正的
靜態方法和字段。
我們來看一下Companion Object的具體應用場景:
字節碼:
Decompile To Java:
Getter And Setter
首先來看在Kotlin中如何聲明一個屬性,屬性可以用關鍵字var 聲明為可變的,否則使用只讀關鍵字val。
class Address {
var name: String = ……
var street: String = ……
var city: String = ……
var state: String? = ……
var zipCode: String = ……
}
其實,聲明一個屬性的完整語法是:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其中,initializer、setter、getter都是可選的。屬性的類型如果可以從initializer或getter中推斷出來,也可以省略。
其中,一個只讀屬性(val)和一個可變屬性(var)的區別是:只讀屬性不允許setter。
一個自定義gettter的例子:
val isEmpty: Boolean
get() = this.size == 0
一個自定義setter的例子:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并賦值給其他屬性
}
如果你需要改變一個訪問器的可見性或者對其注解,但是不需要改變默認的實現, 你可以定義訪問器而不定義其實現:
var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默認實現
var setterWithAnnotation: Any? = null
@Inject set // 用 Inject 注解此 setter
具體使用場景:需要自定義getter和setter的方法舉例:
擴展函數
擴展函數數是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權限。
Koltin可以對一個類的屬性和方法進行擴展,它是一種靜態行為,對擴展的類代碼不會造成任何影響。
擴展函數的定義形式:
fun receiverType.functionName(params){
body
}
- receiverType:表示函數的接收者,也就是函數擴展的對象
- . :擴展函數的修飾符
- functionName:擴展函數的名稱
- params:擴展函數的參數,可以為NULL
一個簡單的擴展函數的舉例:
//聲明一個User類
class User(var name:String)
//定義一個簡單的打印User名字的擴展函數
fun User.Print(){
print("用戶名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}
項目中已有的最簡便的擴展函數的應用(ViewExtensions):
package com.xingin.common
import android.text.SpannableString
import android.view.View
import android.widget.TextView
/**
* Created by chris on 03/08/2017.
*/
fun View.hide() {
this.visibility = View.GONE
}
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.showIf(shouldShow: Boolean) {
this.visibility = if (shouldShow) View.VISIBLE else View.GONE
}
fun View.hideIf(shouldHide: Boolean) {
showIf(!shouldHide)
}
fun TextView.setTextOrHide(text: CharSequence) {
this.text = text
showIf(text.isNotEmpty())
}
import java.io.File
/**
* Created by Kathy on 2017/9/25.
*/
fun File.mkdirIfNotExists() {
if (!exists()) mkdirs()
}
fun File.deleteIfIsFile() {
if (isFile && exists()) delete()
}
public final class FileExtensionsKt {
public static final void mkdirIfNotExists(@NotNull File $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
if(!$receiver.exists()) {
$receiver.mkdirs();
}
}
public static final void deleteIfIsFile(@NotNull File $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
if($receiver.isFile() && $receiver.exists()) {
$receiver.delete();
}
}
Kotlin擴展函數允許我們在不改變已有類的情況下,為類添加新的函數。
最常用的擴展函數的實例:
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
然后在我們的Activity中將它作為普通方法來直接使用:
override fun onCreate(savedInstanceState: Bundle?) {
toast("This is onCreate!!")
toast("Hello world!", Toast.LENGTH_LONG)
toast(message = "Hello world!", duration = Toast.LENGTH_LONG)
}
End