Kotlin優(yōu)勢(shì)
- 空安全 :在編譯時(shí)期處理各種null情況,避免執(zhí)行時(shí)異常垂蜗。
- 函數(shù)式支持:它使用了很多函數(shù)式編程概念。
- 擴(kuò)展函數(shù):可以給任何類添加擴(kuò)展函數(shù)盟广。
- 高度互操作性:Kotlin和Java兩個(gè)語(yǔ)言之間可以互操作废岂,兩種語(yǔ)言互相調(diào)用祖搓,混合編程。
構(gòu)造函數(shù)
在 Kotlin 中的一個(gè)類可以有一個(gè)主構(gòu)造函數(shù)和一個(gè)或多個(gè)次級(jí)構(gòu)造函數(shù)湖苞。主構(gòu)造函數(shù)是類頭的一部分:它跟在類名后拯欧。
//firstName可以在initializer blocks中使用
class Person constructor(firstName: String) {
}
如果主構(gòu)造函數(shù)沒(méi)有任何注解或者可見(jiàn)性修飾符,可以省略這個(gè) constructor
關(guān)鍵字财骨。
class Person(firstName: String) {
}
主構(gòu)造函數(shù)不能包含任何的代碼镐作。
初始化的代碼可以放到以 init 關(guān)鍵字作為前綴的初始化塊(initializer blocks)中:
class Person(name: String) {
init {
CLog.d("Person initialized with name ${name}")
}
}
注意,主構(gòu)造的參數(shù)可以在初始化塊中使用隆箩。它們也可以在類體內(nèi)中聲明屬性時(shí)使用:
class Person(name: String) {
val customerKey = name.toUpperCase()
}
事實(shí)上该贾,聲明屬性以及從主構(gòu)造函數(shù)初始化屬性,Kotlin 有簡(jiǎn)潔的語(yǔ)法捌臊,與普通屬性一樣杨蛋,主構(gòu)造函數(shù)中聲明的屬性可以是可變的(var)或只讀的(val):
class Person(val firstName: String, val lastName: String, var age: Int) {
// ……
}
如果構(gòu)造函數(shù)有注解或可見(jiàn)性修飾符,這個(gè) constructor 關(guān)鍵字是必需的理澎,并且這些修飾符在它前面:
class Person private constructor(name: String) {
//......
}
次級(jí)構(gòu)造函數(shù)
類也可以聲明前綴有 constructor的次構(gòu)造函數(shù)六荒。
如果類有一個(gè)主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)需要委托給主構(gòu)造函數(shù)矾端, 可以直接委托或者通過(guò)別的次構(gòu)造函數(shù)間接委托掏击。委托到同一個(gè)類的另一個(gè)構(gòu)造函數(shù)用 this 關(guān)鍵字即可。
如果你沒(méi)有委托主構(gòu)造函數(shù)秩铆,系統(tǒng)會(huì)提示:Primary constructor call expected
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一個(gè)非抽象類沒(méi)有聲明任何(主或次)構(gòu)造函數(shù)砚亭,它會(huì)有一個(gè)生成的不帶參數(shù)的主構(gòu)造函數(shù)。構(gòu)造函數(shù)的可見(jiàn)性是 public殴玛。如果你不希望你的類有一個(gè)公有構(gòu)造函數(shù)捅膘,你需要聲明一個(gè)帶有非默認(rèn)可見(jiàn)的空的主構(gòu)造函數(shù):
class DontCreateMe private constructor () {
}
繼承
在 Kotlin 中所有類都有一個(gè)共同的超類 Any,它是沒(méi)有繼承父類的類的默認(rèn)超類:
class Example // 從 Any 隱式繼承
默認(rèn)每個(gè)類在創(chuàng)建的時(shí)候都是final的滚粟,如果沒(méi)有添加open
關(guān)鍵字寻仗,你在繼承的時(shí)候系統(tǒng)會(huì)提示This type is final, so it cannot be inherited from
。
要聲明一個(gè)顯式的父類凡壤,我們把類型放到類頭的冒號(hào)之后:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如果該父類有一個(gè)主構(gòu)造函數(shù)署尤,則子類可以(并且必須) 用父類型的)主構(gòu)造函數(shù)參數(shù)就地初始化。如果沒(méi)有執(zhí)行這一步亚侠,系統(tǒng)會(huì)提示This type has a constructor , and thus must be initailized here
曹体。
如果類沒(méi)有主構(gòu)造函數(shù),那么每個(gè)次構(gòu)造函數(shù)必須使用 super 關(guān)鍵字初始化其父類型硝烂,或委托給另一個(gè)構(gòu)造函數(shù)做到這一點(diǎn)箕别。 注意,在這種情況下,不同的次構(gòu)造函數(shù)可以調(diào)用父類型的不同的構(gòu)造函數(shù):
class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
}
如果你不執(zhí)行這一步串稀,系統(tǒng)會(huì)提示你Explicit "this" or ''super" call is required. There is no constructor in superclass that can be called without arguments
類上的open 標(biāo)注與 Java 中 final 相反除抛,它允許其他類從這個(gè)類繼承。
默認(rèn)情況下母截,在 Kotlin 中所有的類都是 final到忽。
要么為繼承而設(shè)計(jì),并提供文檔說(shuō)明微酬,要么就禁止繼承绘趋。
伴生對(duì)象
在 Kotlin 中類沒(méi)有static的方法和變量颤陶,所以需要使用Companion Object來(lái)聲明類似static的方法和變量颗管。
其實(shí)這些變量并不是真正的static變量,而是一個(gè)伴生對(duì)象滓走,這個(gè)伴生對(duì)象位于類中定義的一個(gè)叫做Companion的內(nèi)部類中垦江。
類內(nèi)部的對(duì)象聲明可以用 companion 關(guān)鍵字標(biāo)記:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
該伴生對(duì)象的成員可通過(guò)只使用類名作為限定符來(lái)調(diào)用:
val instance = MyClass.create()
在Java中如何調(diào)用Companion Object的屬性和方法呢?
MyClass.INSTANCE.create()
當(dāng)然搅方,在 JVM 平臺(tái)比吭,如果使用 @JvmStatic
@JvmField
注解,你可以將伴生對(duì)象的成員生成為真正的
靜態(tài)方法和字段姨涡。
我們來(lái)看一下Companion Object的具體應(yīng)用場(chǎng)景:
字節(jié)碼:
Decompile To Java:
Getter And Setter
首先來(lái)看在Kotlin中如何聲明一個(gè)屬性衩藤,屬性可以用關(guān)鍵字var 聲明為可變的,否則使用只讀關(guān)鍵字val涛漂。
class Address {
var name: String = ……
var street: String = ……
var city: String = ……
var state: String? = ……
var zipCode: String = ……
}
其實(shí)赏表,聲明一個(gè)屬性的完整語(yǔ)法是:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其中,initializer匈仗、setter瓢剿、getter都是可選的。屬性的類型如果可以從initializer或getter中推斷出來(lái)悠轩,也可以省略间狂。
其中,一個(gè)只讀屬性(val)和一個(gè)可變屬性(var)的區(qū)別是:只讀屬性不允許setter火架。
一個(gè)自定義gettter的例子:
val isEmpty: Boolean
get() = this.size == 0
一個(gè)自定義setter的例子:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // 解析字符串并賦值給其他屬性
}
如果你需要改變一個(gè)訪問(wèn)器的可見(jiàn)性或者對(duì)其注解鉴象,但是不需要改變默認(rèn)的實(shí)現(xiàn), 你可以定義訪問(wèn)器而不定義其實(shí)現(xiàn):
var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默認(rèn)實(shí)現(xiàn)
var setterWithAnnotation: Any? = null
@Inject set // 用 Inject 注解此 setter
具體使用場(chǎng)景:需要自定義getter和setter的方法舉例:
擴(kuò)展函數(shù)
擴(kuò)展函數(shù)數(shù)是指在一個(gè)類上增加一種新的行為何鸡,甚至我們沒(méi)有這個(gè)類代碼的訪問(wèn)權(quán)限炼列。
Koltin可以對(duì)一個(gè)類的屬性和方法進(jìn)行擴(kuò)展,它是一種靜態(tài)行為音比,對(duì)擴(kuò)展的類代碼不會(huì)造成任何影響俭尖。
擴(kuò)展函數(shù)的定義形式:
fun receiverType.functionName(params){
body
}
- receiverType:表示函數(shù)的接收者,也就是函數(shù)擴(kuò)展的對(duì)象
- . :擴(kuò)展函數(shù)的修飾符
- functionName:擴(kuò)展函數(shù)的名稱
- params:擴(kuò)展函數(shù)的參數(shù),可以為NULL
一個(gè)簡(jiǎn)單的擴(kuò)展函數(shù)的舉例:
//聲明一個(gè)User類
class User(var name:String)
//定義一個(gè)簡(jiǎn)單的打印User名字的擴(kuò)展函數(shù)
fun User.Print(){
print("用戶名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}
項(xiàng)目中已有的最簡(jiǎn)便的擴(kuò)展函數(shù)的應(yīng)用(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擴(kuò)展函數(shù)允許我們?cè)诓桓淖円延蓄惖那闆r下稽犁,為類添加新的函數(shù)焰望。
最常用的擴(kuò)展函數(shù)的實(shí)例:
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
然后在我們的Activity中將它作為普通方法來(lái)直接使用:
override fun onCreate(savedInstanceState: Bundle?) {
toast("This is onCreate!!")
toast("Hello world!", Toast.LENGTH_LONG)
toast(message = "Hello world!", duration = Toast.LENGTH_LONG)
}
End