kotlin的基础语法
函数定义
函数定义使用关键字 fun,参数格式为:参数 : 类型
fun sum(a: Int, b:Int): Int {
return a+b
}
fun print(a: Int, b:Int): Unit {
println(a+b)
}
可变长参数函数
函数的变长参数可以用 vararg 关键字进行标识:
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array) {
vars(1,2,3,4,5) // 输出12345
}
lambda(匿名函数)
lambda表达式使用实例:
// 测试
fun main(args: Array) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
定义常量与变量
可变变量定义:var 关键字
var <标识符> : <类型> = <初始化值>
不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)
val <标识符> : <类型> = <初始化值>
常量与变量都可以没有初始化值,但是在引用前必须初始化
编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改
字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
NULL检查机制
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
类型检测及自动类型转换
重点:做过类型检测之后,obj会自动被系统转换为String类型
我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
//在这里还有一种方法,与Java中instanceof不同,使用!is
// if (obj !is String){
// // XXX
// }
// 这里的obj仍然是Any类型的引用
return null
}
或者
fun getStringLength(obj: Any): Int? {
if (obj !is String)
return null
// 在这个分支中, `obj` 的类型会被自动转换为 `String`
return obj.length
}
甚至还可以
fun getStringLength(obj: Any): Int? {
// 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
if (obj is String && obj.length > 0)
return obj.length
return null
}
区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。
区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例:
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
kotlin基本数据类型
比较2个数字
Kotlin 中没有基础数据类型,只有封装的数字类型,你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
类型转换
由于不同的表示方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误
val i: Int = b.toInt() // OK
位操作符
对于Int和Long类型,还有一系列的位操作符可以使用,分别是:
shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向
字符的使用
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 显式转换为数字
}
数组
数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数。如下所示,我们分别是两种方式创建了两个数组:
fun main(args: Array) {
//[1,2,3]
val a = arrayOf(1, 2, 3)
//[0,2,4]
val b = Array(3, { i -> (i * 2) })
//读取数组内容
println(a[0]) // 输出结果:1
println(b[1]) // 输出结果:2
}
字符串
Kotlin 支持使用三个引号 """ 来表示多行字符串,比如:
fun main(args: Array) {
val text = """
多行字符串
多行字符串
"""
println(text) // 输出有一些前置空格
}
String 可以通过 trimMargin() 方法来删除多余的空白。
fun main(args: Array) {
val text = """
|多行字符串
|编程狮
|多行字符串
|W3cschool
""".trimMargin()
println(text) // 前置空格删除了
}
默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。
kotlin 条件控制
val c = if (condition) a else b
when 条件表达式
when 表达式类似java的switch语句。具体用法可以参考如下几个示例。
when (x) {
1,2 -> print("x == 1")
3 -> print("x == 2")
else -> { //这里可以写成代码块
print("x 不是 1 2 3")
}
}
//使用in操作
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
//使用is操作
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
//可以用来取代if else 链
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
// in 操作
fun main(args: Array) {
val items = setOf("apple", "banana", "kiwi")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
}
kotlin循环控制
For循环
for (item: Int in ints) {
// ……
}
for (i in array.indices) {
print(array[i])
}
//使用库函数 withIndex
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
Break和Continue标签的使用
//这块看的不是很懂,先做记录
在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。
loop@ for (i in 1..100) {
// ……
}
现在,我们可以用标签限制 break 或者continue:
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。
标签处返回
Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回。回想一下我们这么写的时候:
fun foo() {
ints.forEach {
if (it == 0) return
print(it)
}
}
这个 return 表达式从最直接包围它的函数即 foo 中返回。 (注意,这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。
fun foo() {
ints.forEach lit@ {
if (it == 0) return@lit
print(it)
}
}
现在,它只会从 lambda 表达式中返回。通常情况下使用隐式标签更方便。 该标签与接受该 lambda 的函数同名。
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}
或者,我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回
fun foo() {
ints.forEach(fun(value: Int) {
if (value == 0) return
print(value)
})
}
当要返一个回值的时候,解析器优先选用标签限制的 return,即
return@a 1
意为"从标签 @a 返回 1",而不是"返回一个标签标注的表达式 (@a 1)"。
kotlin 类和对象
Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
class Person constructor(firstName: String) {}
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
}
// 测试
fun main(args: Array) {
var person: Person = Person()
person.lastName = "wang"
println("lastName:${person.lastName}")
person.no = 9
println("no:${person.no}")
person.no = 20
println("no:${person.no}")
}
//包含主构造器
class W3cschool constructor(name: String) { // 类名为 W3cschool
// 大括号内是类体构成
var url: String = "http://www.w3cschool.com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
fun printTest() {
println("我是类的函数")
}
}
fun main(args: Array) {
val w3cschool= W3cschool("编程狮")
println(w3cschool.siteName)
println(w3cschool.url)
println(w3cschool.country)
w3cschool.printTest()
}
//包含次构造器
class W3cschool constructor(name: String) { // 类名为 W3cschool
// 大括号内是类体构成
var url: String = "http://www.w3cschool .com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
// 次构造函数
constructor (name: String, alexa: Int) : this(name) {
println("Alexa 排名 $alexa")
}
fun printTest() {
println("我是类的函数")
}
}
fun main(args: Array) {
val w3cschool = W3cschool ("编程狮", 10000)
println(w3cschool .siteName)
println(w3cschool .url)
println(w3cschool .country)
w3cschool .printTest()
}
抽象类
抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
嵌套类
我们可以把类嵌套在其他类中,看以下实例:
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。
匿名内部类
使用对象表达式来创建匿名内部类:
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
类的修饰符
类的修饰符包括 classModifier 和accessModifier:
classModifier: 类属性修饰符,标示类本身特性。abstract // 抽象类 final // 类不可继承,默认属性 enum // 枚举类 open // 类可继承,类默认是final的 annotation // 注解类
accessModifier: 访问权限修饰符private // 仅在同一个文件中可见 protected // 同一个文件中或子类可见 public // 所有调用的地方都可见 internal // 同一个模块中可见
实例
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见
kotlin 继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
注意:Any 不是 java.lang.Object。
如果一个类要被继承,可以使用 open 关键字进行修饰。
open class Base(p: Int) // 定义基类
class Derived(p: Int) : Base(p)
构造函数:
子类有主构造函数:
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
open class Person(var name : String, var age : Int){// 基类
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}
// 测试
fun main(args: Array) {
val s = Student("W3cschool", 18, "S12346", 89)
println("学生名: ${s.name}")
println("年龄: ${s.age}")
println("学生号: ${s.no}")
println("成绩: ${s.score}")
}
子类没有主构造函数:
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
DEMO:
/**用户基类**/
open class Person(name:String){
/**次级构造函数**/
constructor(name:String,age:Int):this(name){
//初始化
println("-------基类次级构造函数---------")
}
}
/**子类继承 Person 类**/
class Student:Person{
/**次级构造函数**/
constructor(name:String,age:Int,no:String,score:Int):super(name,age){
println("-------继承类次级构造函数---------")
println("学生名: ${name}")
println("年龄: ${age}")
println("学生号: ${no}")
println("成绩: ${score}")
}
}
fun main(args: Array) {
var s = Student("W3cschool", 18, "S12345", 89)
}
重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}
fun main(args: Array) {
val s = Student()
s.study();
}
如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } //接口的成员变量默认是 open 的
fun b() { print("b") }
}
class C() : A() , B{
override fun f() {
super.f()//调用 A.f()
super.f()//调用 B.f()
}
}
fun main(args: Array) {
val c = C()
c.f();
}
属性重写
属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
open class Foo {
open val x: Int get { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
你可以用一个var属性重写一个val属性,但是反过来不行。因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法
你可以在主构造函数中使用 override 关键字作为属性声明的一部分:
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
kotlin 接口
Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现, 且可以实现多个接口。
interface MyInterface {
fun bar() // 未实现
fun foo() { //已实现 // 可选的方法体
println("foo")
}
}
class Child : MyInterface { override fun bar() { // 方法体 } }
接口中的属性
接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性:
interface MyInterface {
var name: String //name 属性, 抽象的
fun bar() fun foo() { // 可选的方法体
println("foo")
}
}
class Child: MyInterface {
override var name: String = "w3cschool" //重写属性
override fun bar() { // 方法体
println("bar")
}
}
fun main(args: Array < String > ) {
val c = Child()
c.foo();
c.bar();
println(c.name)
}
函数重写
实现多个接口时,可能会遇到同一方法继承多个实现的问题。直接看demo就可以。
interface A {
fun foo() {
print("A")
} // 已实现
fun bar() // 未实现,没有方法体,是抽象的
}
interface B {
fun foo() {
print("B")
} // 已实现 fun bar() { print("bar") } // 已实现
}
class C: A {
override fun bar() {
print("bar")
} // 重写
}
class D: A,
B {
override fun foo() {
super < A > .foo() super < B > .foo()
}
override fun bar() {
super < B > .bar()
}
}
fun main(args: Array < String > ) {
val d = D() d.foo();
d.bar();
}
kotlin扩展
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
fun receiverType.functionName(params){
body
}
receiverType:表示函数的接收者,也就是函数扩展的对象
functionName:扩展函数的名称
params:扩展函数的参数,可以为NULL
以下实例扩展 User 类 :
class User(var name:String)
/**扩展函数**/
fun User.Print(){
print("用户名 $name")
}
fun main(arg:Array){
var user = User("W3cschool")
user.Print()
}
实例执行输出结果为:
用户名 W3cschool
下面代码为 MutableList 添加一个swap 函数:
// 扩展函数 swap,调换不同位置的值
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
fun main(args: Array) {
val l = mutableListOf(1, 2, 3)
// 位置 0 和 2 的值做了互换
l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
println(l.toString())
}
实例执行输出结果为:
[3, 2, 1]
this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例)。
注: 更多关于kotlin扩展的内容可以参考https://www.w3cschool.cn/uwota/uwota-4xz6376y.html
kotlin数据类和密封类
Kotlin 可以创建一个只包含数据的类,关键字为 data:
data class User(val name: String, val age: Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 对应于属性,按声明顺序排列
copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
主构造函数至少包含一个参数。
所有的主构造函数的参数必须标识为val 或者 var ;
数据类不可以声明为 abstract, open, sealed 或者 inner;
数据类不能继承其他类 (但是可以实现接口)。
复制
复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类,其实现会类似下面这样:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
实例
使用 copy 类复制 User 数据类,并修改 age 属性:
data class User(val name: String, val age: Int)
fun main(args: Array
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
println(jack)
println(olderJack)
}
输出结果为:
User(name=Jack, age=1)
User(name=Jack, age=2)
其他关于数据类和密封类的解释参考如下链接:https://www.w3cschool.cn/uwota/uwota-hv74376z.html
## kotlin范型
泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。
与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
声明一个泛型类:
class Box
var value = t
}
创建类的实例时我们需要指定类型参数:
val box: Box
// 或者
val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box
以下实例向泛型类 Box 传入整型数据和字符串:
class Box(t : T) {
var value = t
}
fun main(args: Array) {
var boxInt = Box(10)
var boxString = Box("W3cschool")
println(boxInt.value)
println(boxString.value)
}
注:关于范型约束、型变、星号投射 详细内容参考:https://www.w3cschool.cn/uwota/uwota-7kmf3770.html
Kotlin 枚举类
枚举类最基本的用法是实现一个类型安全的枚举。
枚举常量用逗号分隔,每个枚举常量都是一个对象。
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
枚举初始化
每一个枚举都是枚举类的实例,它们可以被初始化:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
Kotlin 对象表达式和对象声明
详细内容直接查看:https://www.w3cschool.cn/uwota/uwota-3ojn3772.html
此处暂不做整理
kotlin 委托
包括类委托、属性委托、延迟属性Lazy、可观察属性 Observable
、把属性存储在映射中、Not Null、 局部委托属性、属性委托要求、翻译规则、提供委托
https://www.w3cschool.cn/uwota/uwota-k5wp3773.html
伴生对象
类内部的对象声明可以用 companion 关键字标记:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
该伴生对象的成员可通过只使用类名作为限定符来调用:
val instance = MyClass.create()
可以省略伴生对象的名称,在这种情况下将使用名称 Companion:
class MyClass {
companion object { }
}
val x = MyClass.Companion
其自身所用的类的名称(不是另一个名称的限定符)可用作对该类的伴生对象 (无论是否具名)的引用:
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
请注意,即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员,而且,例如还可以实现接口:
interface Factory {
fun create(): T
}
class MyClass {
companion object : Factory {
override fun create(): MyClass = MyClass()
}
}
val f: Factory = MyClass
当然,在 JVM 平台,如果使用 @JvmStatic
注解,你可以将伴生对象的成员生成为真正的静态方法和字段。更详细信息请参见Java 互操作性一节 。
对象表达式和对象声明之间有一个重要的语义差别:
- 对象表达式是在使用他们的地方立即执行(及初始化)的;
- 对象声明是在第一次被访问到时延迟初始化的;
- 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配。
项目中的一个使用方式。
companion object {
@JvmStatic
fun typeOf(type: String): NodeType? {
return values().firstOrNull { it.type == type }
}
}
Kotlin 函数
let 函数
首先let()的定义是这样的,默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return
fun T.let(f: (T) -> R): R = f(this)
简单示例:
fun testLet(): Int {
// fun T.let(f: (T) -> R): R { f(this)}
"testLet".let {
println(it)
println(it)
println(it)
return 1
}
}
//运行结果
//testLet
//testLet
//testLet
apply 函数
apply函数是这样的,调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象
fun T.apply(f: T.() -> Unit): T { f(); return this }
代码示例:
fun testApply() {
// fun T.apply(f: T.() -> Unit): T { f(); return this }
ArrayList().apply {
add("testApply")
add("testApply")
add("testApply")
println("this = " + this)
}.let { println(it) }
}
// 运行结果
// this = [testApply, testApply, testApply]
// [testApply, testApply, testApply]
with 函数
with函数是一个单独的函数,并不是Kotlin中的extension,所以调用方式有点不一样,返回是最后一行,然后可以直接调用对象的方法,感觉像是let和apply的结合。
fun with(receiver: T, f: T.() -> R): R = receiver.f()
代码示例:
fun testWith() {
// fun with(receiver: T, f: T.() -> R): R = receiver.f()
with(ArrayList()) {
add("testWith")
add("testWith")
add("testWith")
println("this = " + this)
}.let { println(it) }
}
// 运行结果
// this = [testWith, testWith, testWith]
// kotlin.Unit
run 函数
fun T.run(f: T.() -> R): R = f()
代码示例:
fun testRun() {
// fun T.run(f: T.() -> R): R = f()
"testRun".run {
println("this = " + this)
}.let { println(it) }
}
// 运行结果
// this = testRun
// kotlin.Unit
小结
转载:https://www.jianshu.com/p/28ce69d58fea