本文是在學習和使用kotlin時的一些總結與體會,一些代碼示例來自於網絡或Kotlin官方文檔,持續更新...html
對象表達式:至關於Java匿名類部類,在使用的地方被當即執行:java
val a = 10
val listener = object : Info("submit"),IClickListener {
override fun doClick() {
println("a:$a")
}
}
listener.doClick() // 打印 a:10
//有時候咱們只是須要一個沒有父類的對象,咱們能夠這樣寫:
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
//像 java 的匿名內部類同樣,對象表達式能夠訪問閉合範圍內的變量 (和 java 不同的是,這些變量不用是 final 修飾的)
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent){
enterCount++
}
})
}
複製代碼
對象申明:Kotlin 中咱們能夠方便的經過對象聲明來得到一個單例,對象聲明是延遲加載的, 在第一次使用的時候被初始化,對象聲明不是一個表達式,不能用在賦值語句的右邊,對象聲明不能在局部做用域(即直接嵌套在函數內部),可是它們能夠嵌套到其餘對象聲明或非內部類中,node
object MyInfo: Info("submit"),IClickListener {
override fun doClick() {
println("MyInfo do click, $text") // Log: MyInfo do click, , submit
}
}
fun main(args: Array<String>) {
MyInfo.doClick()
}
//當對象聲明在另外一個類的內部時,這個類的實例並不能直接訪問對象申明內部,而只能經過類名來訪問,一樣該對象也不能直接訪問到外部類的方法和變量
class Site {
var name = "菜鳥教程"
object DeskTop{
var url = "www.runoob.com"
fun showName(){
print{"desk legs $name"} // 錯誤,不能訪問到外部類的方法和變量
}
}
}
fun main(args: Array<String>) {
var site = Site()
site.DeskTop.url // 錯誤,不能經過外部類的實例訪問到該對象
Site.DeskTop.url // 正確
}
複製代碼
伴隨(生)對象:至關於靜態內部類+該類的靜態屬性,所在的類被加載,伴生對象被初始化(和 Java 的靜態初始是對應):python
class Books(var name: String, val page: Int) {
companion object ComBooks{
val a : Int = 10
fun doNote() {
println("do note")
}
}
}
fun main(args: Array<String>) {
Books.ComBooks.doNote()
println("Book.a = ${Books.ComBooks.a}")
println("-------------")
Books.doNote()
}
// Log
do note
Book.a = 10
-------------
do note
//伴隨對象的成員能夠經過類名作限定詞直接使用:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
//在使用了 companion 關鍵字時,伴隨對象的名字能夠省略:
class MyClass {
companion object {
}
}
//儘管伴隨對象的成員很像其它語言中的靜態成員,但在運行時它們任然是真正類的成員實例,好比能夠實現接口:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
//若是你在 JVM 上使用 @JvmStatic 註解,你能夠有多個伴隨對象生成爲真實的靜態方法和屬性
複製代碼
備用字段:Kotlin中不能有field,但在自定義getter/setter的時候須要直接訪問屬性而不是又經過getter/settter來取值賦值(循環調用)。Kotlin自動提供一個備用字段(field),經過它能夠直接訪問屬性,沒有使用備用字段時不生成備用字段(使用了setter就會生成),:算法
//使用field關鍵字
public var fieldProp = ""
get() = field
set(value) {
field = value;
}
//不生成:
val isEmpty: Boolean
get() = this.size == 0
//生成:
val Foo.bar = 1
複製代碼
備用屬性:功能與備用字段相似。:express
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // 參數類型是自動推導
}
return _table ?: throw AssertionError("Set to null by another thread")
}
複製代碼
Kotlin能夠像python(@property)同樣把方法變成屬性調用,Kotlin是定義一個屬性複寫get()方法返回某個對象中其餘的計算出來的值。編程
至關於java static finial xxx,而val 只是fInal ,一個編譯時常量,一個運行時常量。使用const必須:後端
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprected(SUBSYSTEM_DEPRECATED) fun foo() { ... }
複製代碼
當定義屬性時沒有使用 ? ,那麼說明是一個非空屬性,這時必需要初始化,若是想在後面的方法中再去賦值要加上lateinit。:api
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method()
}
}
//這個修飾符只可以被用在類的 var 類型的可變屬性定義中,不能用在構造方法中.而且屬性不能有自定義的 getter 和 setter訪問器.這個屬性的類型必須是非空的,一樣也不能爲一個基本類型.在一個lateinit的屬性初始化前訪問他,會致使一個特定異常,告訴你訪問的時候值尚未初始化
複製代碼
類代理: Kotlin 在語法上支持代理 ,Derived 類能夠繼承 Base 接口而且指定一個對象代理它所有的公共方法:安全
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { printz(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
//在 Derived 的父類列表中的 by 從句會將 b 存儲在 Derived 內部對象,而且編譯器會生成 Base 的全部方法並轉給 b
複製代碼
代理屬性: 所謂的委託屬性,就是對其屬性值的操做再也不依賴於其自身的getter()/setter()方法,是將其託付給一個代理類,從而每一個使用類中的該屬性能夠經過代理類統一管理,不再用在每一個類中,對其聲明重複的操做方法。語法:
val/var <property name>: <Type> by <expression>
//var/val:屬性類型(可變/只讀)
//name:屬性名稱
//Type:屬性的數據類型
//expression:代理類
複製代碼
使用場景:
Kotlin標準庫中已實現的代理:
延遲加載(Lazy):lazy()是一個函數, 接受一個Lambda表達式做爲參數, 返回一個Lazy類型的實例,這個實例能夠做爲一個委託, 實現延遲加載屬性(lazy property): 第一次調用 get() 時, 將會執行 lazy() 函數受到的Lambda 表達式,而後會記住此次執行的結果, 之後全部對 get() 的調用都只會簡單地返回之前記住的結果:
val no: Int by lazy {
200
}
val c = 200
fun main(args: Array<String>) {
val b = 200
println(no) // Log : 200
println(no) // Log : 200
}
複製代碼
注意:
可觀察屬性(Observable):Delegates.observable() 函數接受兩個參數: 第一個是初始化值, 第二個是屬性值變化事件的響應器(handler).這種形式的委託,採用了觀察者模式,其會檢測可觀察屬性的變化,當被觀察屬性的setter()方法被調用的時候,響應器(handler)都會被調用(在屬性賦值處理完成以後)並自動執行執行的lambda表達式,同時響應器會收到三個參數:被賦值的屬性, 賦值前的舊屬性值, 以及賦值後的新屬性值。:
var name: String by Delegates.observable("wang", {
kProperty, oldName, newName ->
println("kProperty:${kProperty.name} | oldName:$oldName | newName:$newName")
})
fun main(args: Array<String>) {
println("name: $name") // Log:nam:wang
name = "zhang" // Log:kProperty:name | oldName:wang | newName:zhang
name = "li" // Log:kProperty:name | oldName:zhang | newName:li
}
//Delegates.observable(wang, hanler),完成了兩項工做,一是,將name初始化(name=wang);二是檢測name屬性值的變化,每次變化時,都會打印其賦值前的舊屬性值, 以及賦值後的新屬性值。
```
複製代碼
Vetoable:Delegates.vetoable()函數接受兩個參數: 第一個是初始化值, 第二個是屬性值變化事件的響應器(handler),是可觀察屬性(Observable)的一個特例,不一樣的是在響應器指定的自動執行執行的lambda表達式中在保存新值以前作一些條件判斷,來決定是否將新值保存。:
var name: String by Delegates.vetoable("wang", {
kProperty, oldValue, newValue ->
println("oldValue:$oldValue | newValue:$newValue")
newValue.contains("wang")
})
fun main(args: Array<String>) {
println("name: $name")
println("------------------")
name = "zhangLing"
println("name: $name")
println("------------------")
name = "wangBing"
println("name: $name")
}
//Log
name: wang
------------------
oldValue:wang | newValue:zhangLing
name: wang
------------------
oldValue:wang | newValue:wangBing
name: wangBing
```
複製代碼
Not Null:在實際開發時,咱們可能會設置可爲null的var類型屬性,在咱們使用它時,確定是對其賦值,假如不賦值,必然要報NullPointException.一種解決方案是,咱們能夠在使用它時,在每一個地方無論是否是null,都作null檢查,這樣咱們就保證了在使用它時,保證它不是null。這樣無形當中添加了不少重複的代碼。在Kotlin中,用委託能夠不用去寫這些重複的代碼,Not Null委託會含有一個可null的變量並會在咱們設置這個屬性的時候分配一個真實的值。若是這個值在被獲取以前沒有被分配,它就會拋出一個異常。
class App : Application() {
companion object {
var instance: App by Delegates.notNull()
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
複製代碼
將多個屬性保存在一個map內:使用Gson解析Json時,能夠獲取到相應的實體類的實例,固然該實體類的屬性名稱與Json中的key是一一對應的。在Kotlin中,存在這麼一種委託方式,類的構造器接受一個map實例做爲參數,將map實例自己做爲屬性的委託,屬性的名稱與map中的key是一致的,也就是意味着咱們能夠很簡單的從一個動態地map中建立一個對象實例:
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // 打印結果爲: "John Doe"
println(user.age) // 打印結果爲: 25
}
//委託屬性將從這個 map中讀取屬性值(使用屬性名稱字符串做爲 key 值)。
//若是不用只讀的 Map , 而改用值可變的 MutableMap , 那麼也能夠用做 var 屬性的委託。:
class User(val map: MutableMap<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
var map:MutableMap<String, Any?> = mutableMapOf(
"name" to "John Doe",
"age" to 25)
val user = User(map)
println(user.name) // 打印結果爲: "John Doe"
println(user.age) // 打印結果爲: 25
println("--------------")
map.put("name", "Green Dao")
map.put("age", 30)
println(user.name) // 打印結果爲: Green Dao
println(user.age) // 打印結果爲: 30
}
複製代碼
屬性委託的前提條件:getValue(),setValue()。自定義委託必需要實現:ReadOnlyProperty和ReadWriteProperty。取決於咱們被委託的對象是val仍是var,如:
public interface ReadWriteProperty<in R, T> {
public operator fun getValue(thisRef: R, property: KProperty<*>): T
public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
//定義一個NotNullVar
private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
//第一個thisRef表示持有該對象的對象,
//第二個參數 property 是該值的類型,
//第三個參數 value 就是屬性的值了
複製代碼
密封類:類的擴展,但每一個枚舉常量只存在一個實例,而密封類的一個子類能夠有可包含狀態的多個實例,雖然密封類也能夠有子類,可是全部子類都必須在與密封類自身相同的文件中聲明,間接的子類不受限制。密封類是自身抽象的,它不能直接實例化並能夠有抽象(abstract)成員。密封類不容許有非-private 構造函數(其構造函數默認爲 private)。使用密封類的關鍵好處在於使用 when 表達式 的時候,可以驗證語句覆蓋了全部狀況,就不須要爲該語句再添加一個 else 子句了:
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// 再也不須要 `else` 子句,由於咱們已經覆蓋了全部的狀況
}
複製代碼
與java8相似可有抽象方法與實現方法,不可保存狀態,屬性必須是抽象的或惟一值的(無備用屬性)。
不須要在類中去添加方法,在外部就能夠給任何地方的類添加咱們想要的方法,替換掉java中的FileUtil,xxxUtil等如:
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
//變成
list.swap(list.binarySearch(otherList.max()), list.max())
複製代碼
擴展是被靜態解析的:擴展實際上並無修改它所擴展的類,只是讓這個類的實例對象可以經過"."調用新的函數。須要強調的是擴展函數是靜態分發的,舉個例子,它們並非接受者類型的虛擬方法。這意味着擴展函數的調用是由發起函數調用的表達式的(對象)類型決定的,而不是在運行時動態得到的表達式的(對象)類型決定。好比:
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
//輸出 c,由於這裏擴展函數的調用決定於聲明的參數 c 的類型,也就是 C。
複製代碼
若是有同名同參數的成員函數和擴展函數,調用的時候會使用成員函數,好比:
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
C().foo()
//輸出"member",而不是"extension"
//能夠經過不一樣的函數簽名的方式重載函數的成員函數:
fun C.foo(i:Int) { println("extention") }
C().foo(1)
//輸出 「extentions」。
複製代碼
擴展的域:大多數時候咱們在 top level 定義擴展(就在包下面直接定義):
package foo.bar
fun Baz.goo() { ... }
//爲了在除聲明的包外使用這個擴展,咱們須要在別的文件中使用時導入:
//-------------------------------------------------//
package com.example.usage
import foo.bar.goo // 導入全部名字叫 "goo" 的擴展
// 或者
import foo.bar.* // 導入foo.bar包下得全部數據
fun usage(baz: Baz) {
baz.goo()
}
複製代碼
函數擴展:
fun <T> MutableList<T>.swap(x: Int, y: Int) {
val tmp = this[x] // 'this' corresponds to the list
this[x] = this[y]
this[y] = tmp
}
//this 關鍵字對應接收者對象(MutableList<T>)
//使用:
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)// 在 `swap()` 函數中 `this` 持有的值是 `l`
複製代碼
可空的接收者: 使用空接收者類型進行定義。這樣的擴展使得,即便是一個空對象仍然能夠調用該擴展,而後在擴展的內部進行 this == null 的判斷。這樣你就能夠在 Kotlin 中任意調用 toString() 方法而不進行空指針檢查:空指針檢查延後到擴展函數中完成:
fun Any?.toString(): String {
if (this == null) return "null"
// 在空檢查以後,`this` 被自動轉爲非空類型,所以 toString() 能夠被解析到任何類的成員函數中
return toString()
}
複製代碼
T.因此擴展是用.來使用的,如Kotlin的to函數就是A to B 以空格使用。
注意,因爲擴展並不會真正給類添加了成員屬性,所以也沒有辦法讓擴展屬性擁有一個備份字段.這也是爲何初始化函數不容許有擴展屬性。擴展屬性只可以經過明確提供 getter 和 setter方法來進行定義:
//正確:
val <T> List<T>.lastIndex: Int
get() = size-1
//錯誤:
val Foo.bar = 1 //error: initializers are not allowed for extension properties
複製代碼
class MyClass {
companion object {}
}
fun MyClass.Companion.foo(){
}
//調用
MyClass.foo()
複製代碼
咱們常常建立一個只保存數據的類。在這樣的類中一些函數只是機械的對它們持有的數據進行,如從服務端返回的Json字符串對象映射成Java類。data類使用:
data class 類名(var param1 :數據類型,...){}
data class 類名 可見性修飾符 constructor(var param1 : 數據類型 = 默認值,...)
//data爲聲明數據類的關鍵字,必須書寫在class關鍵字以前。
//在沒有結構體的時候,大括號{}可省略。
//構造函數中必須存在至少一個參數,而且必須使用val或var修飾。這一點在下面數據類特性中會詳細講解。
//參數的默認值無關緊要。(若要實例一個無參數的數據類,則就要用到默認值)
// 定義一個名爲Person的數據類:
data class Preson(var name : String,val sex : Int, var age : Int)
複製代碼
data類必須知足的條件:
約定俗成的規定:當構造函數中的參過多時,爲了代碼的閱讀性,一個參數的定義佔據一行。
編輯器爲咱們作的事情:
copy函數的使用:
data class User(val name : String, val pwd : String)
val mUser = User("kotlin","123456")
println(mUser)
val mNewUser = mUser.copy(name = "new Kotlin")
println(mNewUser)
複製代碼
標準庫提供的data類: Pair 和 Triple,源碼以下:
@file:kotlin.jvm.JvmName("TuplesKt")
package kotlin
// 這裏去掉了源碼中的註釋
public data class Pair<out A, out B>(
public val first: A,
public val second: B) : Serializable {
// toString()方法
public override fun toString(): String = "($first, $second)"
}
// 轉換
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
// 轉換成List集合
public fun <T> Pair<T, T>.toList(): List<T> = listOf(first, second)
// 這裏去掉了源碼中的註釋
public data class Triple<out A, out B, out C>(
public val first: A,
public val second: B,
public val third: C ) : Serializable {
// toString()方法
public override fun toString(): String = "($first, $second, $third)"
}
// 轉換成List集合
public fun <T> Triple<T, T, T>.toList(): List<T> = listOf(first, second, third)
複製代碼
兩種型變:
協變:當A ≦ B時,若是有f(A) ≦ f(B),那麼f叫作協變;
逆變:當A ≦ B時,若是有f(B) ≦ f(A),那麼f叫作逆變;
其他爲不變。
協變,逆變,不變來原於子類能夠安全的向上轉型爲父類。
不變(java的泛型是不變的):
ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch
複製代碼
協變:
List<? extends Number> list001 = new ArrayList<Integer>();
List<? extends Number> list002 = new ArrayList<Float>();
Number n1=list001.get(0);
Number n2=list002.get(0);
複製代碼
「? extends Number」則表示通配符」?」的上界爲Number,換句話說就是,「? extends Number」能夠表明Number或其子類,但表明不了Number的父類(如Object),由於通配符的上界是Number。因而有「? extends Number」 ≦ Number,則List<? extends Number> ≦ List< Number >。可是這裏不能向list00一、list002添加除null之外的任意對象。能夠這樣理解一下,List能夠添加Interger及其子類,List能夠添加Float及其子類,List、List都是List<? extends Animal>的子類型,若是能將Float的子類添加到List<? extends Animal>中,就說明Float的子類也是能夠添加到List中的,顯然是不可行。故java爲了保護其類型一致,禁止向List<? extends Number>添加任意對象,不過卻能夠添加null。
逆變:
List<? super Number> list = new ArrayList<>();
List<? super Number> list001 = new ArrayList<Number>();
List<? super Number> list002 = new ArrayList<Object>();
list001.add(new Integer(3));
list002.add(new Integer(3));
複製代碼
「? super Number」 則表示通配符」?」的下界爲Number。爲了保護類型的一致性,由於「? super Number」能夠是Object或其餘Number的父類,因沒法肯定其類型,也就不能往List<? super Number >添加Number的任意父類對象。可是能夠向List<? super Number >添加Number及其子類。
PECS(《Effective Java》,producer-extends, consumer-super):協變只能取(生產者),逆變只能寫(消費者),如java的幾個api:
public class Stack<E>{
public Stack();
public void push(E e): public E pop();
public boolean isEmpty();
}
//push all
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
//pop all
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
// java.util.Collections的copy方法
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
複製代碼
Kotlin 用out ,in修飾符使協變與逆變使用起來更方便,out表示只生成,in表示只消費,稱之爲聲明處變型。這與 Java 中的使用處變型相反:
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is OK, since T is an out-parameter
// ...
}
//-------------------------//
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
// Thus, we can assign x to a variable of type Comparable<Double>
val y: Comparable<Double> = x // OK!
}
複製代碼
類型投影
使用處變型:類型投影。有些類 不能 限制它只返回 T,如Array,T既要返回又要在參數中消費:
class Array<T>(val size: Int) {
fun get(index: Int): T { /* ... */ }
fun set(index: Int, value: T) { /* ... */ }
}
//這個類既不能是協變的也不能是逆變的,這會在必定程度上下降靈活性。考慮下面的函數形式:
fun copy(from: Array<out Any>, to: Array<Any>) {
// ...
}
複製代碼
星投影
有時你對類型參數一無所知,但任然想安全的使用它。保險的方法就是定一個該範型的投影,每一個該範型的正確實例都將是該投影的子類,Foo<*>
範型約束
上界:最經常使用的類型約束是上界,在 Java 中對應 extends關鍵字,這裏的上界只是約束在給泛型定型時要知足的條件。
fun <T : Comparable<T>> sort(list: List<T>) {
// ...
}
sort(listOf(1, 2, 3)) // OK. Int is a subtype of Comparable<Int>
sort(listOf(HashMap<Int, String>())) // Error: HashMap<Int, String> is not a subtype of Comparable<HashMap<Int, String>>
複製代碼
默認的上界是 Any?。在尖括號內只能指定一個上界。若是要指定多種上界,須要用 where 語句指定:
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable,
T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}
複製代碼
嵌套類,與java的靜態內部類類似:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() //==2
複製代碼
內部類,至關與java的內部類,持有一個外部類的引用,不能單獨使用:
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() //==1
複製代碼
匿名內部類,經過對象表達式建立:
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
//若是對象是函數式的 java 接口的實例(好比只有一個抽象方法的 java 接口),你能夠用一個帶接口類型的 lambda 表達式建立它
val listener = ActionListener { println("clicked") }
複製代碼
函數參數
標準參數:
複製代碼
fun powerOf(number: Int, exponent: Int) { ... }
- 默認參數,函數參數能夠設置默認值,當調用函數時參數被忽略會使用默認值。這樣相比其餘語言能夠減小重載:
```kotlin
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size ) {
...
}
複製代碼
命名參數:在調用函數時能夠用參數的命名來賦值參數。這對於那種有大量參數的函數很方便:
fun reformat(str: String, normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') {
...
}
//調用:
//使用默認參數:
reformat(str)
//調用非默認參數:
reformat(str, true, true, false, '_')
//使用命名參數:
reformat(str,
normalizeCase = true,
uppercaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
//不須要所有參數:
reformat(str, wordSeparator = '_')
//注意,命名參數語法不可以被用於調用Java函數中,由於Java的字節碼不能確保方法參數命名的不變性
複製代碼
默認參數可能只是給參數一個默認值,而命名參數則給參數一個有意義的名字。
不帶返回值的參數:
若是函數不會返回任何有用值,那麼他的返回類型就是 Unit .Unit 是一個只有惟一值Unit的類型.這個值並不須要被直接返回:
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
//Unit 返回值也能夠省略:
fun printHello(name: String?) {
...
}
複製代碼
變長參數:
函數的參數(一般是最後一個參數)能夠用 vararg 修飾符進行標記:
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts)
result.add(t)
return result
}
//標記後,容許給函數傳遞可變長度的參數:
val list = asList(1, 2, 3)
//只有一個參數能夠被標註爲 vararg 。加入vararg並非列表中的最後一個參數,那麼後面的參數須要經過命名參數語法進行傳值,再或者若是這個參數是函數類型,就須要經過lambda法則.
//當調用變長參數的函數時,咱們能夠一個一個的傳遞參數,好比 asList(1, 2, 3),或者咱們要傳遞一個 array 的內容給函數,咱們就可使用 * 前綴操做符:
val a = array(1, 2, 3)
val list = asList(-1, 0, *a, 4)
複製代碼
各類類型參數定義與使用與Python類似
單表達式函數:
//當函數只返回單個表達式時,大括號能夠省略並在 = 後面定義函數體:
fun double(x: Int): Int = x*2
//在編譯器能夠推斷出返回值類型的時候,返回值的類型能夠省略:
fun double(x: Int) = x * 2
複製代碼
函數範圍
Kotlin 中能夠在文件頂級聲明函數,這就意味者你不用像在Java,C#或是Scala同樣建立一個類來持有函數。除了頂級函數,Kotlin 函數能夠聲明爲局部的,做爲成員函數或擴展函數:
//局部函數
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
//局部函數能夠訪問外部函數的局部變量(好比閉包)
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
//局部函數甚至能夠返回到外部函數
fun reachable(from: Vertex, to: Vertex): Boolean {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (current == to) return@reachable true
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(from)
return false
}
複製代碼
成員函數
跟java同樣,類中的成員。
泛型函數
跟java同樣。
尾遞歸函數
Kotlin 支持函數式編程的尾遞歸。這個容許一些算法能夠經過循環而不是遞歸解決問題,從而避免了棧溢出。當函數被標記爲 tailrec 時,編譯器會優化遞歸,並用高效迅速的循環代替它:
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
//使用 tailrec 修飾符必須在最後一個操做中調用本身。在遞歸調用代碼後面是不容許有其它代碼的,而且也不能夠在 try/catch/finall 塊中進行使用。當前的尾遞歸只在 JVM 的後端中能夠用
複製代碼
高階函數
高階函數就是能夠接受函數做爲參數或者返回一個函數的函數:
fun <T> lock(lock: Lock, body: () -> T ) : T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
//body 是一個類型爲 () -> T 的函數,能夠這樣使用
fun toBeSynchroized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchroized)
//更方便的是傳一個字面函數(lambda表達式)
val result = lock(lock, {
sharedResource.operation() })
//在 kotlin 中有一個約定,若是某一個函數的最後一個參數是函數,而且你向那個位置傳遞了一個 lambda 表達式,那麼,你能夠在括號外面定義這個 lambda 表達式:
lock (lock) {
sharedResource.operation()
}
//高階函數map:
fun <T, R> List<T>.map(transform: (T) -> R):
List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}
//調用:
val doubled = ints.map {it -> it * 2}
//若是字面函數只有一個參數,則聲明能夠省略,名字就是 it :
ints.map {it * 2}
//這樣就能夠寫LINQ-風格的代碼了:
strings.filter{ it.length == 5 }.sortedBy{ it }.map{ it.toUpperCase() }
複製代碼
字面函數和函數表達式(lambda表達式),字面函數或函數表達式就是一個 "匿名函數",也就是沒有聲明的函數,但當即做爲表達式傳遞下去:
max(strings, {a, b -> a.length < b.length })
//max 函數就是一個高階函數,它接受函數做爲第二個參數。第二個參數是一個表達式因此本生就是一個函數,即字面函數。做爲一個函數,至關於:
fun compare(a: String, b: String) : Boolean = a.length < b.length
複製代碼
若是隻有一個參數lambda中能夠不寫參數變量,直接用it表示參數,如:
{it.length}
複製代碼
Lambda表達式接收器:
(函數字面量接收器,在定義高階函數參數時使用)是上面二者的結合——一個以指定接收器的擴展函數爲參數的高階函數。因此在咱們傳遞的Lambda表達式中咱們能夠直接訪問接收器的公共方法和屬性(在接受器的上下文環境中),就好像在接收器內部同樣:
inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> Unit) {
val fragmentTransaction = beginTransaction()
fragmentTransaction.func()
fragmentTransaction.commit()
}
//或:
inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commit()
}
//這就是FragmentManager的擴展函數,接收一個Lambda表達式接收器做爲參數,FragmentTransaction做爲接收器
//調用
supportFragmentManager.inTransaction {
//remove(fragmentA)
add(R.id.frameLayoutContent, fragmentB)
}
//須要說明的是在Lambda表達式中咱們調用FragmentTransaction的方法如add或者remove時並無使用修飾符,由於這是對FragmentTransaction的擴展函數.
複製代碼
函數類型
一個函數要接受另外一個函數做爲參數,咱們得給它指定一個類型。好比上面的 max:
fun max<T>(collection: Collection<out T>, less: (T, T) -> Boolean): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max!!, it))
max = it
return max
}
//參數 less 是 (T, T) -> Boolean類型,也就是接受倆個 T 類型參數返回一個 Boolean:若是第一個參數小於第二個則返回真。在函數體第四行, less 是用做函數。
//一個函數類型能夠像上面那樣寫,也可有命名參數
val compare: (x: T,y: T) -> Int = ...
複製代碼
函數文本語法
函數文本的徹底寫法:
val sum = {x: Int,y: Int -> x + y}
//函數文本老是在大括號裏包裹着,在徹底語法中參數聲明是在括號內,類型註解是可選的,函數體是在 -> 以後,像下面這樣:
val sum: (Int, Int) -> Int = {x, y -> x+y }
//函數文本有時只有一個參數。若是 kotlin 能夠從它本生計算出簽名,那麼能夠省略這個惟一的參數,並會經過 it 隱式的聲明它
ints.filter {it > 0}//這是 (it: Int) -> Boolean 的字面意思
//注意若是一個函數接受另外一個函數作爲最後一個參數,該函數文本參數能夠在括號內的參數列表外的傳遞
複製代碼
函數表達式
指定返回值的函數在大多數情形中是沒必要要的,由於返回值是能夠自動推斷的。然而,若是你須要本身指定,能夠用函數表達式來作:
fun(x: Int, y: Int ): Int = x + y
//函數表達式很像普通的函數聲明,除了省略了函數名。它的函數體能夠是一個表達式(像上面那樣)或者是一個塊:
fun(x: Int, y: Int): Int {
return x + y
}
//參數以及返回值和普通函數是同樣的,若是它們能夠從上下文推斷出參數類型,則參數類型能夠省略:
ints.filter(fun(item) = item > 0)
複製代碼
閉包
一個字面函數或者表達式函數能夠訪問閉包,即訪問自身範圍外的聲明的變量。不像 java 那樣在閉包中的變量是被捕獲修改的:
var sum = 0
ints.filter{it > 0}.forEach {
sum += it
}
print(sum)
複製代碼
函數表達式擴展
表達式函數的擴展和普通的擴展區別是它有接收類型的規範:
val sum = fun Int.(other: Int): Int = this + other
//接收類型必須在表達式函數中明確指定,但字面函數不用。字面函數能夠做爲擴展函數表達式,但只有接收類型能夠經過上下文推斷出來,表達式函數的擴展類型是一個帶接收者的函數:
sum : Int.(other: Int) -> Int
//使用
1.sum(2)
複製代碼
字面函數(lambda)用->分隔函數體,函數表達式用=分隔函數體。
內聯函數
編譯器將使用函數的定義體來替代函數調用語句,這種替代行爲發生在編譯階段而非程序運行階段,也就是說把被調用的函數體複製到調用處,好處:
使用:
inline fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
//---------調用----------------//
fun run() {
check(l, {"我是lambda方法體"})//l是一個Lock對象
}
//編譯器會把調用處換成這樣:
fun run() {
l.lock()
try {
return "我是lambda方法體"
} finally {
l.unlock()
}
}
//如一個函數是inline的,那麼參數裏的函數,lambda也默認爲inline的。若是要部分參數爲非inline,可使用noinline關鍵字:
inline fun doSomething(a:Int,b:Int,noinline doOther:(a:Int,b:Int)->Int){
//...
}
複製代碼
非局部返回
Kotlin在lambda中不能直接使用return,要使用return配合標籤,但若是內聯,則能夠在lambda中直接使用return,該return直接做用於調用者函數,也就是說直接做用在調用的地方,誰調用退出誰。其餘內聯函數中的return同樣(return也被複制到了調用內聯函數的函數體裏)。若是要只退出lambda,可使用return@xxx。
內聯屬性
對屬性來講,咱們會有get,set的方法來操做這個屬性。 get,set就是個函數,咱們能夠標識他們爲內聯函數:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ...
inline set(v) { ... }
//
inline var bar: Bar
get() = ...
set(v) { ... }
複製代碼
實例化參數類型
有時咱們須要訪問傳遞過來的類型,把它做爲參數:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p?.parent
}
@suppress("UNCHECKED_CAST")
return p as T
}
//調用
myTree.findParentOfType(javaClass<MyTreeNodeType>() )
//
myTree.findParentOfType(MyTreeNodeType::class.java)
//咱們想要的僅僅是給這個函數傳遞一個類型,若是即像下面這樣就很方便:
myTree.findParentOfType<MyTreeNodeType>()
//爲了達到這個目的,內聯函數支持具體化的類型參數申明 reified
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p?.parent
}
return p as T
}
複製代碼
咱們用 refied 修飾符檢查類型參數,既然它能夠在函數內部訪問了,也就基本上接近普通函數了。由於函數是內聯的,因此不準要反射,像 !is `as`這樣的操做均可以使用。同時,咱們也能夠像上面那樣調用它了 myTree.findParentOfType() 。普通的函數(沒有標記爲內聯的)不能有實例化參數。
在不少狀況下會使用反射訪問類型數據,咱們仍然可使用實例化的類型參數 javaClass() 來訪問它:
inline fun methodsOf<reified T>() = javaClass<T>().getMethods()
fun main(s: Array<String>) {
println(methodsOf<String>().joinToString('\n'))
}
複製代碼
。。。
Kotlin 類型系統致力於消滅空引用(NPE),在 Kotlin 類型系統中能夠爲空和不可爲空的引用是不一樣的,屬性默認是要賦初值的,不能爲空:
var a: String ="abc"
a = null //編譯錯誤
//容許爲空,咱們必須把它聲明爲可空的變量
var b: String? = "abc"
b = null
//調用 a 的方法,而不用擔憂 NPE 異常:
val l = a.length()
//使用 b 調用一樣的方法就可能報錯
val l = b.length() //錯誤:b 可能爲空
複製代碼
使用可空屬性(?)時的四種方式:
在條件中檢查 null:
val l = if (b != null) b.length() else -1
//更復雜的條件:
if (b != null && b.length() >0)
print("Stirng of length ${b.length}")
else
print("Empty string")
複製代碼
安全調用,使用安全操做符,?.
b?.length()
//若是 b 不爲空則返回長度,不然返回空。這個表達式的的類型是 Int?,安全調用在鏈式調用是是頗有用的。好比,若是 Bob 是一個僱員可能分配部門(也可能不分配),若是咱們想獲取 Bob 的部門名做爲名字的前綴,就能夠這樣作:
bob?.department?.head?.name
//這樣的調用鏈在任何一個屬性爲空都會返回空
複製代碼
Elvis 操做符,?:
val l = b.length()?: -1
//如if表達式:
val l: Int = if (b != null) b.length() else -1
//若是 ?: 左邊表達式不爲空則返回,不然返回右邊的表達式。注意右邊的錶帶式只有在左邊表達式爲空是纔會執行
//注意在 Kotlin 中 throw return 是表達式,因此它們也能夠在 Elvis 操做符右邊。這是很是有用的,好比檢查函數參數是否爲空:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
//...
}
複製代碼
!! 操做符
NPE-lovers,咱們能夠用 b!! ,這會返回一個非空的 b 或者拋出一個 b 爲空的 NPE:
val l = b !!.length()
複製代碼
安全轉換
普通的轉換可能產生 ClassCastException 異常。另外一個選擇就是使用安全轉換,若是不成功就返回空:
val aInt: Int? = a as? Int
複製代碼
在 kotlin 中有兩種相等
參照相等:參照相等是經過 === 操做符判斷的(不等是!== ) a===b 只有 a b 指向同一個對象是判別才成立。另外,你可使用內聯函數 identityEquals() 判斷參照相等:
a.identityEquals(b)
a identityEquals b
複製代碼
結構相等:結構相等是經過 == 判斷的。像 a == b 將會翻譯成:
a?.equals(b) ?: b === null
//若是 a 不是 null 則調用 equals(Any?) 函數,不然檢查 b 是否參照等於 null
//注意徹底沒有必要爲優化你的代碼而將 a == null 寫成 a === null 編譯器會自動幫你作的
複製代碼
kotlin中==至關於java的equals,===至關於java的==
var (name, age) = person
複製代碼
意思就是一次性申明多個變量,並把=號右邊的對象的屬性拆箱出來賦值給變量。如:
data class Person(var name: String, var age: Int) {
}
var person: Person = Person("Jone", 20)
var (name, age) = person
println("name: $name, age: $age")// 打印:name: Jone, age: 20
複製代碼
若是拆箱出對象的屬性:
val name = person.component1()
val age = person.component2()
複製代碼
person.component1,component2怎麼來的呢,Kotlin的數據類編譯器會根據主構造器中聲明的屬性, 自動推斷生成componentN() 函數羣, 這些函數與類的屬性對應, 函數名中的數字1到N,與屬性的聲明順序一致。那麼若是不是數據類就要本身編寫對象的componentN函數:
class Person(val name: String, val age: Int) {
operator fun component1(): String {
return name
}
operator fun component2(): Int {
return age
}
}
複製代碼
解構申明能夠用在for循環中:
var personA: Person = Person("Door", 22, "ShanDong")
var personB: Person = Person("Green", 30, "BeiJing")
var personC: Person = Person("Dark", 23, "YunNan")
var personD: Person = Person("Tool", 26, "GuanDong")
var personE: Person = Person("Mark", 24, "TianJin")
var pers = listOf(personA, personB, personC, personD, personE)
for ((name, age) in pers) {
println("name: $name, age: $age")
}
複製代碼
Map使用結構申明,Kotlin的標準庫中,對Map實現了這些擴展函數:
operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()
複製代碼
因此在使用Map.Entry.getkey時使用調用到component1(),getvalue時調用component2():
var personA: Person = Person("Door", 22, "ShanDong")
var personB: Person = Person("Green", 30, "BeiJing")
var personC: Person = Person("Dark", 23, "YunNan")
var personD: Person = Person("Tool", 26, "GuanDong")
var personE: Person = Person("Mark", 24, "TianJin")
var map = HashMap<String, Person>()
map.put("1", personA)
map.put("2", personB)
map.put("3", personC)
map.put("4", personD)
map.put("5", personE)
for ((key, value) in map) {
println("key: $key, value: $value")
}
複製代碼
// Log打印 key: 1, value: Person(name='Door', age=22, addr='ShanDong', mobile=null) key: 2, value: Person(name='Green', age=30, addr='BeiJing', mobile=null) key: 3, value: Person(name='Dark', age=23, addr='YunNan', mobile=null) key: 4, value: Person(name='Tool', age=26, addr='GuanDong', mobile=null) key: 5, value: Person(name='Mark', age=24, addr='TianJin', mobile=null)
## Ranges
- 表示從多少到多少,可用於if判斷和for循環,與in關鍵字配合,常見用法:
```kotlin
// Checking if value of comparable is in range. Optimized for number primitives.
if (i in 1..10) println(i)
if (x !in 1.0..3.0) println(x)
if (str in "island".."isle") println(str)
// Iterating over arithmetical progression of numbers. Optimized for number primitives (as indexed for-loop in Java).
for (i in 1..4) print(i) // prints "1234"
for (i in 4..1) print(i) // prints nothing
for (i in 4 downTo 1) print(i) // prints "4321"
for (i in 1..4 step 2) print(i) // prints "13"
for (i in (1..4).reversed()) print(i) // prints "4321"
for (i in (1..4).reversed() step 2) print(i) // prints "42"
for (i in 4 downTo 1 step 2) print(i) // prints "42"
for (x in 1.0..2.0) print("$x ") // prints "1.0 2.0 "
for (x in 1.0..2.0 step 0.3) print("$x ") // prints "1.0 1.3 1.6 1.9 "
for (x in 2.0 downTo 1.0 step 0.3) print("$x ") // prints "2.0 1.7 1.4 1.1 "
for (str in "island".."isle") println(str) // error: string range cannot be iterated over
複製代碼
原理參見標準庫中接口:Range ,Progressiont和和操做函數的擴展。
for in :
若是你想經過 list 或者 array 的索引進行迭代,你能夠這樣作:
for (i in array.indices)
print(array[i])
//-------------------------//
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
複製代碼
類型檢查:is !is 表達式:
//運行時檢查一個對象是不是某個特定類:
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // same as !(obj is String)
print("Not a String")
}
else {
print(obj.length)
}
//智能轉換,編譯器會跟蹤 is 檢查靜態變量,並在須要的時候自動插入安全轉換:
fun demo(x: Any) {
if (x is String) {
print(x.length) // x is automatically cast to String
}
}
if (x !is String) return
print(x.length) //x 自動轉換爲 String
//在 || && 操做符,when 表達式和 whie 循環中:
// x is automatically cast to string on the right-hand side of `||`
if (x !is String || x.length == 0) return
// x is automatically cast to string on the right-hand side of `&&`
if (x is String && x.length > 0)
print(x.length) // x is automatically cast to String
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is Array<Int> -> print(x.sum())
}
複製代碼
轉換:
用as 操做符來轉換類型:
val x: String = y as String
//null 不能被轉換爲 String 由於String不是 nullable,也就是說若是 y 是空的,則上面的代碼會拋出空異常。爲了 java 的轉換語句匹配咱們得像下面這樣:
val x: String?= y as String?
複製代碼
"安全"轉換:
val x: String ?= y as? String
//爲了不拋出異常,能夠用 as? 這個安全轉換符,這樣失敗就會返回 null
複製代碼
若是 this 沒有應用者,則指向的是最內層的閉合範圍。爲了在其它範圍中返回 this ,須要使用標籤:
//this@lable
class A { // implicit label @A
inner class B { // implicit label @B
fun Int.foo() { // implicit label @foo
val a = this@A // A's this
val b = this@B // B's this
val c = this // foo()'s receiver, an Int
val c1 = this@foo // foo()'s receiver, an Int
val funLit = @lambda {String.() ->
val d = this // funLit's receiver
val d1 = this@lambda // funLit's receiver
}
val funLit2 = { (s: String) ->
// foo()'s receiver, since enclosing function literal
// doesn't have any receiver
val d1 = this
}
}
}
}
複製代碼
。。。
operator fun get(position: Int) = dailyForecast[position]
//xxx[position]
當須要把一個對象轉成另外一個,或有多個當前類或對象的.調用等,可使用這些擴展和函數,提升效率:
使用.isNullOrEmpty(),isNullOrBlank(),isBlank(),isEmpty(),isNotBlank(),isNotEmpty()來代替TextUtils判斷字符串。
善用集合中的各類擴展函數,如reduce,filter,map,any,all,count,max,sumBy等等。
構造函數裏的變量若是要在類中使用(類屬性)要標記定義關鍵字var或val,不然做用域不會是整個類,就像只是函數的參數同樣。
一些高階函數,lambda {}裏不要用return,是返回的最後一行,若是用return他又是inline的話會返回了外層的函數。
利用默認參數減小(java)方法重載
可空也是一種類型(可空的xx類型),可接受實參爲空或具體類型的實例,可空類型的實例變量要解包(!!)後纔可使用原類型的屬性、方法。
for( index in 5..1),其中5和1只能是數值,若是用變量要用(x-1)包起來並轉成數字:for (i in (x+1)..(y+1))。
kotlin 沒有Volatile等併發編程的關鍵字,這是kotlin有意爲之,kotlin讓爲這應該讓函數庫來作,但並非不能用,可使用@Volatile,@Synchronized註解來使用相應功能,@Volatile標記jvm的備用字段爲volatile。wait(), notify()等Object(在Kotlin的Any中沒有這些方法)的方法能夠這麼使用:
private val lock = java.lang.Object()
fun produce() = synchronized(lock) {
while (items >= maxItems) {
lock.wait()
}
Thread.sleep(rand.nextInt(100).toLong())
items++
println("Produced, count is $items: ${Thread.currentThread()}")
lock.notifyAll()
}
fun consume() = synchronized(lock) {
while (items <= 0) {
lock.wait()
}
Thread.sleep(rand.nextInt(100).toLong())
items--
println("Consumed, count is $items: ${Thread.currentThread()}")
lock.notifyAll()
}
複製代碼
當碰到用java時經常使用的若是不如爲就…時能夠用let等擴展:
if (data != null) {
nameTv.setText(data.name);
}
//kotlin
data?.apply {
nameTv.text=name
info{"xxx"}
}
data?.let{
nameTv.text=it.name
}
mOnActionListener?.onAction()
複製代碼
使用高階函數+函數對象定義監聽器(java中的onClickListener等)
當可變屬性(var)定義爲可空(?)時編譯器報錯:Smart cast to 'Type' is impossible, because 'variable' is a mutable property that could have been changed by this time 解決的幾種辦法:
var name: String? = null
val names: ArrayList<String> = ArrayList()
//1:若是能用只讀,改爲val。
//2::用一個本地變量接收再使用:
fun foo() {
val nameLoc = a.name
if(nameLoc != null) {
names.add(name);
}
}
//3:用?操做符
name?.let{
names.add(name);
}
//4:若是是在用Elvis操做符
foo1(name?:"")
//循環中
names.add(name?:continue);
複製代碼
做者:竹塵居士
博客:http://zhuchen.vip/2018/04/01/kotlin/kotlin-learn-summary.html