Kotlin-第3節、函式與Lambda閉包
目錄:
1、函式的特性語法
2、巢狀函式
3、擴充套件函式
4、Lambda閉包語法
5、高階函式
6、行內函數
1、函式的特性語法
- 函式的引數可以宣告預設值
- 如果函式體只有一個語句的話,可以將該語句直接賦值給該函式。
fun echo(name:String){ println("$name") } fun echo(name:String = "Zhangtao"){ println("$name") } fun echo(name:String) = println("$name")
2、函式巢狀
用途:在某些情況下出發遞迴的函式,或不希望被外部函式訪問到的函式。
fun function(){ val str = "hello world" fun say(count:Int = 10){ println(str) if(count>0){ say(count - 1) } } say() }
3、擴充套件函式
kotlin特性:靜態給一個類擴充套件成員方法和成員變數。
用類名點方法名的方式
//給File類擴充套件一個readText方法 fun File.readText(charset:Charset = Charsets.UTF-8):String=readBytes().toString(charset) //呼叫 fun main(args:Array<String>){ val file = File("Project2.iml") println(file.readText()) }
java如何使用kotlin的擴充套件函式呢?
public class Main{ public static void main(String[] args){ File file = new File("project2.iml"); String content = FilesKt.readText(file,Charsets.UTF-8); System.out.println(content); } }
注:該擴充套件函式並不是File本身的函式,它編譯的時候會被編譯到那個類對應的class裡面去,在java中我們呼叫的是FileKt,第一個引數是需要擴充套件這個類的物件,該引數不可省略。
- 為什麼說這是靜態的擴充套件呢?
//open值得是不final,與java的final相反的意思 open class Animal class Dog:Animal() //分別給其新增擴充套件方法 fun Animal.name() = "animal" fun Dog.name() = "dog" fun Animal.printName(anim:Animal){ println(anim.name()) } fun main(args:Array<String>){ Dog().printName(Dog())//這裡輸出animal }
- kotlin的擴充套件函式它實際上是靜態的給一個類新增的,它是不具備執行時的一個多型效應的。
編譯完之後:
public static final String name(Animal receiver){ return "animal"; } public static final String name(Dog receiver){ return "dog"; } public static final void printName(Animal r,Animal a){ String str = name(a) System.out.println(str); } public static final void main(String [] args){ //強轉成了animal printName((Animal)new Dog(), (Animal)new Dog()); }
4、kotlin的lambda閉包
public static void main(String[] args){ Thread thread = new Thread(new Runnable(){ public void run(){ // ... } }); thread.start(); }
Java8的Lambda語法:
- 將Runnable物件省略成()-> 的形式替代。
public static void main(String[] args){ Thread thread = new Thread(() -> { //... }); thread.start(); }
kotlin的Lambda語法:
fun main(args:Array<String>){ val thread = Thread({-> Unit }) thread.start() }
- 如果Lambda沒有引數,可以省略箭頭符號->
fun main(args:Array<String>){ val thread = Thread({}) thread.start() }
- 如果Lambda是函式的最後一個引數,可以將大括號放在小括號外面
fun main(args:Array<String>){ val thread = Thread(){} thread.start() }
- 最簡單形態:如果函式只有一個引數且這個引數是Lambda,則可以省略小括號
fun main(args:Array<String>){ val thread = Thread{ } thread.start() }
Lambda閉包宣告
//name:String:引數 //->:閉包體 val echo = { name:String -> println(name) } fun main(args: Array<String>){ echo.invoke("hello") echo("hello ") }
- Lambda引數有上限22個。
- 如果傳23個,會丟擲NoClassDefFoundError,因為編譯成class的時候,會編譯成Funcation23,但是內建最多Funcation22,所以才會找不到。
fun onlyif(isDebug:Boolean,block:()->Unit){ if (isDebug)block() } fun main(args:Array<String>):Unit{ onlyif(true){ println("列印日誌") } }
- 注意:main函式中onlyif(true)用大括號括起來的部分是Lambda函式(onlyif第二參),當Lambda作為函式的最後一個引數時,可以寫在函式的小括號之外。
5、高階函式
重點:函式是“一等公民”
- 物件.函式,這種是函式的呼叫。
- 如果要把函式當做引數傳遞,需要傳遞一個函式的宣告。
- 如果傳遞的是“執行函式”的話,傳遞的實際上是函式的返回值。
val runnable = Runnable{ println("Runnable::run") } //宣告一個引數為空,無返回值的函式物件 val funcation:() -> Unit //將runnable的run方法賦值給宣告好的函式 function = runnable::run //將該函式作為引數傳遞給onlyif onlyif(true,function)
6、用內聯優化程式碼
- Kotlin的Lambda是一個匿名物件
- 可以使用inline修飾方法,這樣當方法在編譯時就會拆解方法的呼叫為語句呼叫,進而減少建立不必要的物件。
建立方式:在高階函式之前增加inline關鍵字。
inline fun onlyif(isDebug:Boolean,block:()->Unit){ if (isDebug)block() } fun main(args:Array<String>):Unit{ onlyif(true){ println("列印日誌") } }
編譯後的class:
public static final void onlyif(boolean isDebug,Function0<Unit>block){ if(isDebug){ block.invoke(); } } public static final void main(String[] args){ //拆解成了語句呼叫 boolean isDebug = true; if(isDebug){ String str = "列印日誌"; System.out.println(str); } }
過分使用inline關鍵字會造成:
- 增加編譯器編譯的負擔。
- 使得程式碼塊變得龐大。