Kotlin学习与实践 (三)fun 函数
生活随笔
收集整理的這篇文章主要介紹了
Kotlin学习与实践 (三)fun 函数
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
通過例子來學(xué)習(xí)如何高效的在Kotlin中定義使用函數(shù)。
1、命名參數(shù)、默認(rèn)參數(shù)、頂層函數(shù)、擴(kuò)展函數(shù)
* 展示幾種創(chuàng)建集合類的方法和擴(kuò)展的簡(jiǎn)單操作集合類的方法
fun createCollection() {
val set = hashSetOf(1, 12, 26)
println("hashSetOf -- > ${set.javaClass}")
val list = arrayListOf(12, 24, 66)
println("arrayListOf -- > ${list.javaClass}")
val map = hashMapOf(1 to "one", 2 to "two", 3 to "three")
println("hashMapOf -- > ${map.javaClass}")
val list1 = listOf("dsa", "ccc", "ddd")
println("listOf -- > ${list1.javaClass}")
}
fun easyOptCollection() {
val strings = listOf("ss", "this", "is", "string ArrayList")
println(strings.last())
val numbers = listOf(1, 200, 20, 30)
println(numbers.max())
}
Kotlin并沒有采用它自己的集合類,而是采用標(biāo)準(zhǔn)的Java集合類,這樣Kotlin就能與Java交互。
再看一個(gè)示例:
fun demand() {
val list = listOf(1, 2, 25)
println(list)
}
* 上面函數(shù)直接輸入 list 是調(diào)用了集合的默認(rèn)的toString方法,為了動(dòng)態(tài)修改輸入的樣子,下面的幾個(gè)函數(shù)是
* 自己的擴(kuò)展,再擴(kuò)展中探討如何讓Kotlin 的方法更簡(jiǎn)單 更高效 更舒服
下面首先按照J(rèn)ava的習(xí)慣和風(fēng)格定義一個(gè)自定義格式化輸出集合的方法
/**
* 通過再元素中間加分隔符,在最前面加前綴,再最后面加后綴把集合轉(zhuǎn)成可輸出的String
* @param collection 集合
* @param separator 分隔符
* @param prefix 前綴
* @param postfix 后綴
*/
fun <T> joinToString(collection: Collection<T>,
separator: String,
prefix: String,
postfix: String): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator) //第一個(gè)元素之前不用加分隔符
result.append(element)
}
result.append(postfix)
return result.toString()
}
* 為了提高代碼的可讀性
* Kotlin支持 命名參數(shù) --> 調(diào)用函數(shù)時(shí) 顯式地指定參數(shù)名稱 (牛叉的是:顯示指定名稱之后就可以打亂參數(shù)的傳遞順序了)
* 注意:為了避免混淆指明了一個(gè)參數(shù)的名稱之后,后面的參數(shù)必須都要標(biāo)明名稱
* 警告:使用Kotlin 調(diào)用Java函數(shù)時(shí),不能采用命名參數(shù)
fun callExplicitly() {
val list = listOf(1, 3, 5)
println(joinToString(list, prefix = "{", separator = "\", postfix = "}"))
}
* 為了避免像Java那樣過多的重載與重復(fù)
* Kotlin 支持默認(rèn)參數(shù)值 --> 在聲明函數(shù)的時(shí)候,指定參數(shù)的默認(rèn)值,在調(diào)用的時(shí)候不傳該參數(shù)時(shí)就使用默認(rèn)的參數(shù)
* 這樣就可以避免創(chuàng)建很多的重載函數(shù)
*
* Java中如果想調(diào)用指定默認(rèn)參數(shù)的函數(shù)必須全部傳遞參數(shù),如果想像在Kotlin中一樣使用其省略參數(shù)的調(diào)用方式就需要給
* Kotlin中聲明的 指定默認(rèn)參數(shù)的函數(shù) 添加“@JvmOverloads”注解,原理是編譯器會(huì)帶有"@JvmOverloads"的方法自動(dòng)生成重載函數(shù)
@JvmOverloads
fun <T> joinToStringWithDefaultParams(collection: Collection<T>,
separator: String = ",",
prefix: String = "",
postfix: String = ""): String {
return joinToString(collection, separator = separator, prefix = prefix, postfix = postfix)
}
fun callWithDefaultParams() {
val list = listOf(1, 3, 5)
println(joinToStringWithDefaultParams(list))
println(joinToStringWithDefaultParams(list, "-"))
println(joinToStringWithDefaultParams(list, "-", "【", "】"))
println(joinToStringWithDefaultParams(list, postfix = "!"))
}
在Java 中一些無法從屬任何類又可能會(huì)被很多類頻繁的調(diào)用的方法通常會(huì)抽取到專門的一個(gè)類中,以 public static final sss()最終會(huì)形成包含很多這種方法的工具類
* 在Kotlin中根本不需要去創(chuàng)建這樣無意義的類。
* 可以把這樣的不從屬于任何類的函數(shù)放到代碼文件的頂層,這些放在文件頂層的函數(shù)依然是包內(nèi)的成員
* 如果要從外部訪問它,直接導(dǎo)入包就可以用,不要額外包一層類名
*
* 其實(shí)編譯器會(huì).kt 文件編譯成Java類,類名為.kt文件名+Kt 例如:join.kt ---> JoinKt.class
* 因此在Java中調(diào)用頂層函數(shù)也很簡(jiǎn)單直接導(dǎo)入編譯的包含頂層文件的類就行了
*
* 如果要改變包含Kotlin 頂層函數(shù)的文件被編譯生成的類名,需要為這個(gè)文件添加 “@JvmName”的注解,將其放到文件的開頭,位于包名的前面
* 比如本類執(zhí)行的名稱“@file:JvmName("StringsFunctions")”
import com.mauiie.kotlin.chapter2fun.K4ExtendPropertyKt;
import com.mauiie.kotlin.chapter2fun.StringsFunctions;
public class JavaCallTest { public static void main(String[] args) { ArrayList<String> strings = new ArrayList<>(5); strings.add("dsada"); strings.add("adsa"); strings.add("jklj"); strings.add("dsada"); System.out.println(StringsFunctions.joinToStringWithDefaultParams(strings)); TopFunAndProperty.performOperation(); TopFunAndProperty.performOperation(); TopFunAndProperty.reportOperationCount(); System.out.println(TopFunAndProperty.getUNIX_LINE_SEPARATOR()); System.out.println(TopFunAndProperty.UNIX_LINE_SEPARATOR_CONSTANTS); System.out.println(TopFunAndProperty.getOpCount()); System.out.println(K3ExtendFunAndPropertyKt.lastChar_("this is a")); /** * 擴(kuò)展函數(shù)和擴(kuò)展方法在Java中調(diào)用的時(shí)候都必須顯示的調(diào)用 */ String testStr = "test"; System.out.println(K4ExtendPropertyKt.getLastChar(testStr)); System.out.println(K4ExtendPropertyKt.lastChar(testStr)); Button button = new Button(); System.out.println(button.getCurrentState()); ; }
* 在使用JDK、Android的時(shí)候,有時(shí)會(huì)面臨代碼不能轉(zhuǎn)成Kotlin的時(shí)候,Kotlin支持擴(kuò)展函數(shù)讓Kotlin支持不能轉(zhuǎn)的代碼
* 理論上來說擴(kuò)展函數(shù)非常簡(jiǎn)單,它就是一個(gè)類的成員函數(shù),不過定義在類的外面。
* 做法很簡(jiǎn)單:把要擴(kuò)展的類或接口的名稱,放到即將添加的函數(shù)前面。
* 這個(gè)類的名稱叫 “接收者類型”;用來調(diào)用這個(gè)擴(kuò)展函數(shù)的那個(gè)對(duì)象叫做“接收者對(duì)象”
* 接收者類型 是由擴(kuò)展函數(shù)定義的,接收者對(duì)象是該類型的一個(gè)實(shí)例
fun String.lastChar_(): Char = this.get(this.length - 1)
* String 是定義的接收者類型
* this 是定義的接收這對(duì)象 也就是String的一個(gè)實(shí)例
* 在擴(kuò)展函數(shù)中,可以像其他成員函數(shù)一樣訪問類的其他變量和屬性,就好像是在這個(gè)類自己的方法中訪問他們一樣。
* 注意:擴(kuò)展函數(shù)不能打破類的封裝性。???????
* 和類的成員變量不同的是,擴(kuò)展函數(shù)不能訪問類的私有或者受保護(hù)的成員。
* 在擴(kuò)展函數(shù)中,可以像其他成員函數(shù)一樣使用“this”,而且也可以像其他成員函數(shù)一樣省略它
fun String.easyLastChar(): Char = get(length - 1)
fun main(args: Array<String>) {
//定義好擴(kuò)展函數(shù)之后就可以像普通的成員函數(shù)一樣去使用了
println("kotlin".lastChar_())
println("test".easyLastChar())
}
* 在Kotlin中,重寫成員函數(shù)是很平常的事情,但是,不能重寫擴(kuò)展函數(shù)。
* 但是,但是, 不能重寫擴(kuò)展函數(shù)
* 擴(kuò)展函數(shù)并不是類的一部分,它是聲明在類之外的。盡管可以給基類和子類都分別定義一個(gè)同名的擴(kuò)展函數(shù),當(dāng)這個(gè)函數(shù)被調(diào)用時(shí)
* 它是由該變量的靜態(tài)類型所決定的,而不是這個(gè)變量的運(yùn)行時(shí)類型
* 注意: 如果一個(gè)類的成員函數(shù)和擴(kuò)展函數(shù)有相同的簽名,成員函數(shù)往往會(huì)被優(yōu)先使用。
* 記住: 如果添加一個(gè)和擴(kuò)展函數(shù)一樣名字的成員函數(shù),那么對(duì)應(yīng)類定義的消費(fèi)者將會(huì)重新編譯代碼,這將會(huì)改變它的意義開始指向新的成員函數(shù)!
open class View {
open fun click() = println("View clicked")
}
class Button : View() {
override fun click() = println("Button clicked")
}
fun View.showOff() = println("View showOff")
fun Button.showOff() = println("Button showOff")
fun main(args: Array<String>) {
val v: View = Button()
val v2: View = View()
val v3 = View()
//具體調(diào)用哪個(gè)方法是由view的實(shí)際值來決定的,
v.click()
v2.click()
v3.click()
//但是, 不能重寫擴(kuò)展函數(shù)
v.showOff()
v2.showOff()
v3.showOff()
val button = Button()
button.showOff()
}
執(zhí)行結(jié)果:
Button clicked
View clicked
View clicked
View showOff
View showOff
View showOff
Button showOff
Kotlin中直接在文件函數(shù)叫做頂層的函數(shù),再Kotlin中看起來不從屬任何類從屬于包直接導(dǎo)入可以使用,但是從編譯之后的字節(jié)碼看會(huì)自動(dòng)編譯xxxKT.Java,頂層函數(shù)從屬于編譯的文件類
@file:JvmName("TopFunAndProperty")
package com.mauiie.kotlin.chapter2fun
/**
* 這樣就聲明了一個(gè)頂層屬性
*/
var opCount = 0 //會(huì)生成getter和setter
/**
* 聲明一個(gè)頂層函數(shù)
*/
fun performOperation() {
opCount++
}
fun reportOperationCount() {
println("Operation performed $opCount times ")
}
val UNIX_LINE_SEPARATOR = "
" //只會(huì)生成getter
const val UNIX_LINE_SEPARATOR_CONSTANTS = "
" //相當(dāng)于Java中的 public static final String ...l
/**
* 現(xiàn)在我們可以將joinToString 函數(shù)寫出終極狀態(tài)了 ---> 作為擴(kuò)展函數(shù)的工具函數(shù)
*/
fun <T> Collection<T>.joinToString(
separator: String,
prefix: String,
postfix: String
): String {
val result = StringBuilder(prefix)
// withIndex 省去了this this.withIndex()
for ((index, element) in withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
2、擴(kuò)展屬性
* 擴(kuò)展屬性提供了一種方法,用來擴(kuò)展類的API,可以用類訪問屬性,用的是屬性語法而不是函數(shù)的語法。
* 盡管它被稱為屬性,但是他們可以沒有任何狀態(tài),因?yàn)闆]有適合的地方來存儲(chǔ)它,不可能給現(xiàn)有的Java對(duì)象實(shí)例添加額外的字段。
* 但有時(shí)段語法仍然是便于使用的。
先扔出來一個(gè)擴(kuò)展函數(shù)
fun String.lastChar(): Char = get(length - 1)
* 上面是為String 擴(kuò)展的方法 lastChar() 現(xiàn)在把它轉(zhuǎn)成屬性試試
* 可以看到擴(kuò)展屬性也像接收者的一個(gè)普通成員屬性一樣。
* 這里,必須定義getter函數(shù),因?yàn)闆]有支持字段,因此沒有默認(rèn)的getter實(shí)現(xiàn)。
* 同理,初始化也不可以:因?yàn)闆]有地方可以存儲(chǔ)初始值
val String.lastChar: Char
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
fun main(args: Array<String>) {
println("Kotlin".lastChar)
val sb: StringBuilder = StringBuilder("Kotlin")
sb.lastChar = 'a'
println(sb.lastChar)
}
注意
* 如果要從Java中訪問擴(kuò)展屬性,應(yīng)該顯示地調(diào)用它的getter函數(shù) K4ExtendPropertyKt.getLastChar("Java")
*對(duì)于定義的擴(kuò)展函數(shù),不會(huì)自動(dòng)在整個(gè)項(xiàng)目中生效,使用的時(shí)候需要像其他函數(shù)和類一樣導(dǎo)入
* 可以使用 as 關(guān)鍵字來修改導(dǎo)入的類或者名稱 同樣在Java也可以使用定義好的擴(kuò)展函數(shù) 參照 java.JavaCallKotlin
package com.mauiie.kotlin.chapter2fun //import com.mauiie.kotlin.chapter2fun.* import com.mauiie.kotlin.chapter2fun.lastChar_ as last val c = "Kotlin".last()
3、可變參數(shù)、中綴表示、解構(gòu)聲明
* a.可變參數(shù)的關(guān)鍵字 vararg,可以用來聲明一個(gè)函數(shù)可能有任意數(shù)量的參數(shù)
* b.一個(gè)中綴表示法,當(dāng)你在調(diào)用一些只有一個(gè)參數(shù)的函數(shù)時(shí),使用它會(huì)讓代碼更簡(jiǎn)練
* c.解構(gòu)聲明,用來把一個(gè)單獨(dú)的組合值展開到多個(gè)變量中
首先看可變參數(shù) vararg的使用:
fun kebiancanshu() {
val list = listOf(1, 45, 36)
//listOf源碼
//public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
}
* 上面可以看在Kotlin使用了 vararg 代替了 Java中的... 聲明了可變參數(shù)
* 與Java不用的一點(diǎn)是如果在傳入?yún)?shù)是已經(jīng)包裝在數(shù)組中的參數(shù)時(shí),在Java中會(huì)原樣傳遞數(shù)組,而Kotlin則要求你顯示著解包數(shù)組,以便于每個(gè)數(shù)組元素能作為單獨(dú)的參數(shù)來調(diào)用
* 從技術(shù)角度講這個(gè)功能被稱為"展開運(yùn)算符",而在使用的時(shí)候不過是在參數(shù)前面放一個(gè) “*”
fun testVararg(args: Array<String>) {
val list = listOf("test", * args)
println(list)
}
fun main(args: Array<String>) {
testVararg(listOf("ds", "dsa", "111").toTypedArray()) //toTypedArray Returns a *typed* array containing all of the elements of this collection.
}
* 聲明map的代碼中的to,不是內(nèi)置解構(gòu),而是一種特殊函數(shù)調(diào)用,被稱為中綴調(diào)用。
* 在中綴調(diào)用中,沒有添加額外的分隔符,函數(shù)名稱是直接放在目標(biāo)對(duì)象名稱參數(shù)之間的。
* 1 to "one" 和 1.to("one")是等價(jià)的
fun testMap() {
val map = mapOf(1 to "one", 2 to "two", 3.to("three"))
// 源碼 public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> = if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
val list = listOf(1.45)
//這也是一種解構(gòu)聲明
for ((index, element) in list.withIndex()) {
println("this $index is $element")
}
}
* 中綴調(diào)用可以與之喲普一個(gè)參數(shù)的參數(shù)的函數(shù)一起使用,無論是普通的函數(shù)還是擴(kuò)展函數(shù)。
* 要允許使用中綴符號(hào)調(diào)用函數(shù),需要使用infix修飾符來標(biāo)記它。
* 下面是一個(gè)簡(jiǎn)單的to函數(shù)的聲明
* Pair 是Kotlin標(biāo)準(zhǔn)庫中的類,它表示已對(duì)元素。Pair和to 都使用了泛型,這里為了簡(jiǎn)單都省略了他們
infix fun Any.to(other: Any) = Pair(this, other)
fun testPair() {
val (number, name) = 1 to "one"
println("this is $number and $name")
}
4、局部函數(shù)
為了讓代碼更簡(jiǎn)潔,Kotlin提供了局部函數(shù)的功能:
* 局部函數(shù)指的是 可以在函數(shù)中嵌套這些提取的函數(shù),這樣既可以獲得所需的結(jié)構(gòu),也無須額外的語法開銷
先看下一個(gè)簡(jiǎn)單的例子,稍后使用局部函數(shù)改造,體驗(yàn)局部函數(shù):
class User(val id: Int, val name: String, val address: String)
fun saveUser(user: User) {
if (user.name.isEmpty()) {
throw IllegalArgumentException("Can't save user[${user.id}] with empty name")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException("Can't save user[${user.id}] with empty address")
}
println("save user successful!")
}
* 提取局部函數(shù)避免重復(fù)
fun saveUser1(user: User) {
fun validate(user: User, value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user[${user.id}] with empty $fieldName ")
}
}
validate(user, user.name, "Name")
validate(user, user.address, "address")
println("save user successful!")
}
* 可以在局部函數(shù)總直接訪問外部函數(shù)的參數(shù)!!
fun saveUser2(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
//可以在局部函數(shù)總直接訪問外部函數(shù)的參數(shù)!!
throw IllegalArgumentException("Can't save user[${user.id}] with empty $fieldName ")
}
}
validate(user.name, "Name")
validate(user.address, "address")
println("save user successful!")
}
* 提取邏輯到擴(kuò)展函數(shù)
fun User.validateBeforSave() {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user[${this.id}] with empty $fieldName ")
}
}
validate(name, "Name")
validate(address, "Address")
}
總結(jié)
以上是生活随笔為你收集整理的Kotlin学习与实践 (三)fun 函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件需求管理工具列表大全
- 下一篇: Hexo | (四)多机同步更新博客