Kotlin基础语法学习笔记2
拓展函数
修改程序,但不修改源代码
接收者对象:函数被调用的对象
val readOnlyShapes: List<String> = listOf("triangle", "square", "circle")
println(readOnlyShapes.first()) //readOnlyShapes就是接收者对象
接收者类型
fun main() {
println("hello".bold())
}
fun String.bold(): String = "<b>$this</b>"
以上例子中给String定义了一个拓展函数,其中一些说明如下:
String是接收者类型
bold是函数名称
.bold()函数返回类型是String
“hello”是接收者对象
函数体内部通过$this来访问接收者对象的值
面向拓展的设计
拓展函数是静态的,可以在任何地方定义拓展函数,不同定义域中拓展函数的使用区别:
顶层(文件级别):可以在同一包内使用
类内部:可见性受访问修饰符限制
(爱来自豆包)
class StringUtils {
// 在类内部定义的扩展函数,仅在 StringUtils 类内部可见
private fun String.toUpperFirst(): String {
return if (isEmpty()) this
else first().uppercase() + substring(1)
}
fun process(str: String): String {
return str.toUpperFirst() // 内部调用扩展函数
}
}
伴生对象
类似静态方法
name: String) {
companion object { /* 伴生对象 */ }
}
// 为 Person 的伴生对象定义扩展函数
fun Person.Companion.createDefault(): Person {
return Person("Default Name")}
fun main() {
// 调用方式:类似静态方法 val defaultPerson = Person.createDefault()
println(defaultPerson.name)}
接口和抽象类
所有实现类都能使用拓展函数,但无法被重写,因为拓展函数是静态的
面向拓展设计http
Ktor库中的代码,起到一个再封装,隐藏参数的效果,结果跟vue中简化http请求一样
class HttpClient {
fun request(method: String, url: String, headers: Map<String, String>): HttpResponse {
println("Requesting $method to $url with headers: $headers")
return HttpResponse("Response from $url")
}
}
fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap())
fun main() {
val client = HttpClient()
// Making a GET request using request() directly
val getResponseWithMember = client.request("GET", "https://example.com", emptyMap())
// Making a GET request using the get() extension function
val getResponseWithExtension = client.get("https://example.com")
}
作用域函数
为一个对象创建一个临时作用域并执行一些代码
Kotlin总共5个作用域函数:let、apply、run、also、with
每个作用域都接受一个lambda表达式,然后返回表达式的结果
let
使用场景:当想在代码中进行空值检查,然后对返回值进行进一步操作时
以可空入参传入非空函数为例:
fun sendNotification(recipientAddress: String): String {
println("Yo $recipientAddress!")
return "Notification sent!"
}
fun getNextAddress(): String {
return "sebastian@jetbrains.com"
}
fun main() {
val address: String? = getNextAddress()
sendNotification(address) //问题,这条语句会报参数类型不匹配的错,因为address的类型是String? 而sendNotification函数是String
}
//使用if解决
val confirm = if(address != null) {
sendNotification(address)
} else { null }
//使用Elvis运算符解决
//这里写法不对,应该要拆分变量存空值
//val confirm = sendNotification(address ?: return)
//推荐使用let解决,简洁且语义清晰
//如果传入null,?.直接短路返回,不会执行let域中代码
val confirm = address?.let { sendNotification(it) }
apply
使用场景:创建对象时需要进行一系列初始化操作时
class Client() {
var token: String? = null
fun connect() = println("connected!")
fun authenticate() = println("authenticated!")
fun getData(): String = "Mock data"
}
//val client = Client()
//使用apply作用域函数,省略掉对象名调用,可以直接使用对象的属性和方法
//创建对象和初始化连在一起
val client = Client().apply {
token = "asdf"
connect()
authenticate()
}
fun main() {
// client.token = "asdf"
// client.connect()
// // connected!
// client.authenticate()
// authenticated!
client.getData()
}
run
类似apply函数,也可以用来初始化对象,但是官方推荐用来初始化+使用对象计算一个结果
client.run {
connect()
authenticate()
getData()
}
apply与run的区别
| 特性 | apply | run |
|---|---|---|
| 返回值 | 对象本身 | 代码块最后一行执行结果 |
| 用途 | 配置对象 | 基于对象计算结果 |
also
对对象进行一个额外操作,然后返回该对象
使用场景:记日志
fun main() {
val medals: List<String> = listOf("Gold", "Silver", "Bronze")
val reversedLongUppercaseMedals: List<String> =
medals
.map { it.uppercase() }
.also { println(it) } //这样不用额外显示的使用medal去调用打印函数了,而且加入了函数式的调用链中,可读性更高
.filter { it.length > 4 }
.also { println(it) }
.reversed()
println(reversedLongUppercaseMedals)
// [BRONZE, SILVER]
}
with
with不是拓展函数
需要将接收者对象传入with函数去使用(类似when语句带主语的调用方式)
使用场景:当想要在对象上调用多个函数时
//模拟画面渲染过程,渲染了文字,矩形,圆形
class Canvas {
fun rect(x: Int, y: Int, w: Int, h: Int): Unit = println("$x, $y, $w, $h")
fun circ(x: Int, y: Int, rad: Int): Unit = println("$x, $y, $rad")
fun text(x: Int, y: Int, str: String): Unit = println("$x, $y, $str")
}
fun main() {
val mainMonitorSecondaryBufferBackedCanvas = Canvas()
with(mainMonitorSecondaryBufferBackedCanvas) {
text(10, 10, "Foo")
rect(20, 30, 100, 50)
circ(40, 60, 25)
text(15, 45, "Hello")
rect(70, 80, 150, 100)
circ(90, 110, 40)
text(35, 55, "World")
rect(120, 140, 200, 75)
circ(160, 180, 55)
text(50, 70, "Kotlin")
}
}
简单总结
| 函数 | 上下文引用 | 返回值 | 应用场景 | |
|---|---|---|---|---|
| let | it | lambda结果 | 空值检查,返回对象进行下一步操作 | |
| apply | this | 接收者对象 | 初始化对象 | |
| run | this | lambda结果 | 在创建时初始化对象并计算结果 | |
| also | it | 接收者对象 | 在返回对象之前进行一些额外操作 | |
| with | this | lambda结果 | 对一个对象调用多个函数 | |
| 一些疑问: |
- 为什么also上下文引用是it,却又返回接收者对象?
- also的设计意图"附加操作"
- it与this的语义差异
- this内部视角,适合对内操作
- it外部视角,适合对外操作
- 与
apply形成互补
Kotlin 的作用域函数设计上形成了互补:apply(this+ 返回对象):适合 “配置对象内部状态”(如设置属性);also(it+ 返回对象):适合 “对对象执行外部附加操作”(如日志、校验)。
两者都返回上下文对象,但引用方式不同,恰好好区分了 “内部配置” 和 “外部操作” 两种场景。
带有接收者的lambda表达式
可以直接使用对象的属性和成员函数,无需显示的使用对象引用
格式:[接收者类型].[函数类型]
MutableList<Int>.()->Unit//这是一个函数类型
//MutableList<Int>是接收者类型
//()表示没有参数
//返回值是Unit
fun MutableList<Int>.test(){println("hello")}
fun main() {
val list: MutableList<Int> = mutableListOf()
list.test()
}
例子1,拓展StringBuilder
fun StringBuilder.appendText(){append("hello")}
fun main() {
val sb = StringBuilder("abc")
sb.appendText()
println(sb.toString())//abc
println(StringBuilder("abc").appendText().toString())//输出kotlin.Unit,因为appendText无返回值,不能链式调用
}
例子2,构建菜单
class MenuItem(val name: String)
class Menu(val name: String){
val items = mutableListOf<MenuItem>()
fun item(name: String) {
items.add(MenuItem(name))
}
}
//init是一个函数类型参数,指外部传一个函数进来
//这个init就是一个DSL
fun menu(name: String, init: Menu.() -> Unit): Menu {
val menu = Menu(name)
menu.init()
return menu
}
fun printMenu(menu: Menu) {
println("Menu: ${menu.name}")
menu.items.forEach { println(" Item: ${it.name}") }
}
fun main() {
val menu = menu("Kotlin"){
item("a")
item("b")
item("c")
}
printMenu(menu)
}
类和接口
类继承
通常Kotlin不允许继承,但支持单继承
如果要使用类继承来共享代码,首先考虑抽象类
抽象类
可以被默认继承
有构造函数,但无法创建实例
子类用override关键字"重写"父类的属性和函数,称为实现
抽象类可以同时包含已经实现和未实现的函数与属性(与Java中一样)
创建
抽象函数和属性不能在top level,必须在抽象类中
abstract class Animal(){
abstract val name: String
abstract fun eat()
}
abstract class Product(val name: String, var price: Int) {
abstract val category: String
fun productInfo(): String {
return "Category:$category, Name:$name, Price:$price"
}
}
/**
疑问:1.为什么子类的属性不需要val/var定义了?
因为子类已经从父类继承了name和price,这里的name和price只作为临时变量传给父类,而warranty是一个新的属性,是需要定义为子类的属性,于是使用了val
**/
class Electronic(name: String, price: Int, val warranty: Int) : Product(name, price) {
override val category: String = "Electronic"
}
接口
没有构造函数和头
函数和属性默认"开放",即继承
声明一个接口
interface Animal
接口实现
interface PaymentMethod{
fun initiatePayment(amount: Double): String
}
class CreditCardPayment(val cardNumber: String, val cardHolderName: String, val expiryDay: String): PaymentMethod{
override fun initiatePayment(amount: Double): String {
return "Payment of $$amount initiated using Credit Card ending in ${cardNumber.takeLast(4)}."
}
}
fun main() {
val paymentMethod = CreditCardPayment("1234 5678 9012 3456", "John Doe", "12/25")
println(paymentMethod.initiatePayment(100.0))
// Payment of $100.0 initiated using Credit Card ending in 3456.
}
多接口实现用逗号隔开
class CreditCardPayment : PaymentMethod, PaymentType
可以在构造函数中声明override实现属性,入下例子,Audio在构造函数中用override实现了结构中的title属性
注意:接口没有构造函数
interface Media{
val title: String
fun play()
}
class Audio(override val title: String, val composer: String) : Media {
override fun play() {
println("Playing audio: $title, composed by $composer")
}
}
fun main() {
val audio = Audio("Symphony No. 5", "Beethoven")
audio.play()
// Playing audio: Symphony No. 5, composed by Beethoven
}
委托
问题:现在有个接口A,有个实现了接口A的AA类,然后有个需求要创建一个AA类的子类AAA,但是继承AA类之后AAA类依然需要冗余一堆接口A的实现代码
解决方案:使用委托,把接口实现委托给父类AA的对象
interface Drawable {
fun draw()
fun resize()
val color: String?
}
class Circle : Drawable {
override fun draw() {
TODO("An example implementation")
}
override fun resize() {
TODO("An example implementation")
}
override val color = null
}
class RedCircle(param: Circle) : Drawable by param {
override val color = "red" //选择性的覆盖需要覆盖的属性或者方法
}
对象
对象声明
使用对象声明来声明一个具有单个实例的类。
懒加载:用到的时候才去加载
所有对象都以线程安全方式创建
object DoAuth{}
对象体{}花括号内定义属性和成员函数
对象没有构造函数,没头部
一个简单的例子
object DoAuth{
fun takeParams(username: String, password: String){
println("input Auth parameters = $username:$password")
}
}
fun main() {
//DoAuth对象只在第一次调用时创建
DoAuth.takeParams("Sean", "123456")
}
对象可以从接口中继承
interface Auth {
fun takeParams(username: String, password: String)
}
object DoAuth : Auth {
override fun takeParams(username: String, password: String) {
println("input Auth parameters = $username:$password")
}
}
fun main() {
DoAuth.takeParams("Sean", "123456")
}
数据对象
额外提供成员函数:toString() 和 equals()
不自动提供copy(),因为只有一个对象
data object AppConfig {}
伴生对象
一个类只能有一个伴生对象
伴生对象只在其类第一次引用时创建
在伴生对象中声明的任何属性和函数都将在所有实例中共享
一个伴生对象不必有名字。如果你不定义一个,默认是 Companion
fun main() {
BigBen.getBongs(10)
}
class BigBen{
companion object Bonger{
fun getBongs(nTimes: Int){
repeat(nTimes){print("Bong ")}
}
}
}
开放类
如果不能使用接口和抽象类,可以使用open关键字显式的声明一个类开放,使它可以继承
open class Vehicle(val make: String, val model: String)
class Car(make: String, model: String, val numberOfDoors: Int): Vehicle(make, model)
重写继承行为
重写父类的属性和成员函数
父类用open开放成员函数
open fun displayInfo()
子类用override重写
override fun displayInfo()
属性
属性可以重写,但不建议
对于open类的子类来说,可以直接访问父类的属性,推荐
//属性继承,与函数继承一样
open class Vehicle(val make: String, val model: String) {
open val transmissionType: String = "Manual"
}
class Car(make: String, model: String, val numberOfDoors: Int) : Vehicle(make, model) {
override val transmissionType: String = "Automatic"
}
//通过将属性放在构造函数中,子类可以传值初始化这个属性,就是直接访问
open class Vehicle(val make: String, val model: String, val transmissionType: String = "Manual")
class Car(make: String, model: String, val numberOfDoors: Int) : Vehicle(make, model, "Automatic")
开放类和接口
一个类可以继承某个类的同时实现多个接口,但是必须把父类放在前面,接口放在后面
class ElectricCar(
make: String,
model: String,
numberOfDoors: Int,
val capacity: Double,
val emission: String
) : Car(make, model, numberOfDoors), EcoFriendly, ElectricVehicle {
override val batteryCapacity: Double = capacity
override val emissionLevel: String = emission
}
特殊类
密封类
应用场景:限制类的继承范围
是一种特殊的抽象类
只能在同一个包内创建它的子类,包外的不能继承
sealed class Mammal
密封类可以配合when使用
普通抽象类也可以,但是要注意,普通抽象类必须添加else分支,
因为普通抽象类无法确定自己有多少子类,
而密封类可以穷举自己所有子类,然后可以不加else
sealed class Mammal(val name: String)
class Cat(val catName: String) : Mammal(catName)
class Human(val humanName: String, val job: String) : Mammal(humanName)
fun greetMammal(mammal: Mammal): String {
when (mammal) {
is Human -> return "Hello ${mammal.name}; You're working as a ${mammal.job}"
is Cat -> return "Hello ${mammal.name}"
//else -> return "Unknown" abstract类必须加这句
}
}
fun main() {
println(greetMammal(Cat("Snowy")))
// Hello Snowy
}
枚举类
应用场景:表示一组有限的、不同的值
enum class State{
IDLE, RUNNING, FINISHED
}
fun main() {
val state = State.RUNNING
val message = when (state) {
State.IDLE -> "It's idle"
State.RUNNING -> "It's running"
State.FINISHED -> "It's finished"
}
println(message)
}
枚举类可以有属性和成员函数
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF),
YELLOW(0xFFFF00);//用;号分隔开成员函数
fun containsRed(rgb: Int): Boolean = (this.rgb and 0xFF0000 != 0)
}
内联值类
使用场景:从类中创建小型对象,并且只使用它们很短的时间
内联值类 必须 在类头部有一个初始化的单一属性
只能包含值
不能包含非private的属性(避免被当作普通类使用)
不能有状态相关的复杂结构(如其他对象引用)
成员函数是允许的
可以实现接口,因为接口没有状态,但不能继承
interface Contact{
}
@JvmInline
value class Email(val address: String): Contact{
fun test() = "hello world"
}
属性
支持字段
属性具有默认的属性访问器,就是get set方法
为了避免在get set中直接访问属性导致无限递归,kotlin把属性的实际值存在backing field中,也就是所谓的支持字段
如果满足以下任一条件,则存在后备字段:
- 您使用属性的默认
get()或set()函数。 - 你尝试使用
field关键字在代码中访问属性值。
为什么会无限递归?
-
getter 中的递归:
当你调用example.value时,会触发 getter 方法。而 getter 中写了return value + 1,这又会再次调用value的 getter,形成循环:getter() → value → getter() → value → ...
直到栈溢出报错。 -
setter 中的递归:
当你执行example.value = 5时,会触发 setter 方法。而 setter 中写了value = newValue * 2,这又会再次调用value的 setter,形成循环:setter(5) → value = 10 → setter(10) → value = 20 → ...
同样会导致栈溢出。
解决方案:使用 field 关键字
field 是 Kotlin 提供的特殊关键字,专门用于在访问器中直接访问后备字段(存储属性实际值的底层变量),而非再次触发访问器:
class Example {
var value: Int = 0
get() {
return field + 1 // 正确:访问后备字段
}
set(newValue) {
field = newValue * 2 // 正确:修改后备字段
}
}
拓展属性
没有backing fields
需要自己编写get set
无法保存任何状态
扩展属性不能覆盖类的现有属性
data class Person(val firstName: String, val lastName: String)
// Extension property to get the full name
val Person.fullName: String
get() = "$firstName $lastName"
fun main() {
val person = Person(firstName = "John", lastName = "Doe")
// Use the extension property
println(person.fullName)
// John Doe
}
委托属性
将属性的属性访问器委托给另一个对象
使用场景:存储简单属性无法处理的复杂属性,比如数据表、浏览器存储值,就是这个属性获取或者存储时需要插入一段复杂逻辑
每个委托的对象都必须有一个getVlue函数,如果是可变属性还要有setValue函数
定义:
val displayName: String by Delegate
getValue/setValue结构
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {}
operator 关键字将这些函数标记为操作符函数,使它们能够重载 get() 和 set() 函数thisRef 参数指的是包含委托属性的对象 。默认情况下,类型被设置为 Any?,但您可能需要声明更具体的类型属性参数指的是其值被访问或更改的属性。您可以使用此参数来访问属性名称或类型等信息。默认情况下,类型设置为 Any?(可空引用类型,Any是Kotlin中引用类型的根)
例子,用委托属性缓存显示名称
class CachedStringDelegate {
var cachedValue: String? = null
operator fun getValue(thisRef: User, property: Any?): String{
if(cachedValue == null){
cachedValue = "${thisRef.firstName} ${thisRef.lastName}"
println("Computed and cached: $cachedValue")
}else{
println("Accessed from cache: $cachedValue")
}
return cachedValue ?: "Unknown"
}
}
class User(val firstName: String, val lastName: String) {
val displayName: String by CachedStringDelegate()
}
fun main() {
val user = User("John", "Doe")
// First access computes and caches the value
println(user.displayName)
// Computed and cached: John Doe
// John Doe
// Subsequent accesses retrieve the value from cache
println(user.displayName)
// Accessed from cache: John Doe
// John Doe}
标准委托
Kotlin标准库提供的委托
不需要手动定义getValue,setValue
lazy
lazy接口用来实现属性的懒加载委托
在get函数第一次调用时执行,get函数的后续任何一次调用都返回第一次的结果
lazy接受一个lambda参数,用尾随lambda方式传入
默认线程安全
class Database {
fun connect() {
println("Connecting to the database...")
}
fun query(sql: String): List<String> {
return listOf("Data1", "Data2", "Data3")
}
}
val databaseConnection: Database by lazy {
val db = Database()
db.connect()
db
}
fun fetchData() {
val data = databaseConnection.query("SELECT * FROM data")
println("Data: $data")
}
fun main() {
// First time accessing databaseConnection
fetchData()
// Connecting to the database...
// Data: [Data1, Data2, Data3]
// Subsequent access uses the existing connection
fetchData()
// Data: [Data1, Data2, Data3]
}
可观察属性
监控属性值是否发生变化,触发反应
使用场景:日志,调试,数据验证
import kotlin.properties.Delegates.observable
class Thermostat {
//observable() 函数接受 20.0 作为参数,并使用它来初始化属性
/**
传递给 observable() 函数的 lambda 表达式:
有三个参数:
_,它指的是属性本身。
old,这是属性的旧值。
new,这是属性的新值
**/
var temperature: Double by observable(20.0) { _, old, new ->
if (new > 25) {
println("Warning: Temperature is too high! ($old°C -> $new°C)")
} else {
println("Temperature updated: $old°C -> $new°C")
}
}
}
fun main() {
val thermostat = Thermostat()
thermostat.temperature = 22.5
// Temperature updated: 20.0°C -> 22.5°C
thermostat.temperature = 27.0
// Warning: Temperature is too high! (22.5°C -> 27.0°C)
}
空安全
智能类型转换和空安全类型转换
is和!is运算符
is 和 !is 运算符与 when 或 if 条件表达式:
is检查对象是否具有该类型,并返回一个布尔值。- !is 检查对象 不 具有该类型,并返回一个布尔值。
fun printObjectType(obj: Any) {
when (obj) {
is Int -> println("It's an Integer with value $obj")
!is Double -> println("It's NOT a Double")
else -> println("Unknown type")
}
}
fun main() {
val myInt = 42
val myDouble = 3.14
val myList = listOf(1, 2, 3)
// The type is Int
printObjectType(myInt)
// It's an Integer with value 42
// The type is List, so it's NOT a Double. printObjectType(myList)
// It's NOT a Double
// The type is Double, so the else branch is triggered. printObjectType(myDouble)
// Unknown type
}
as 和 as? 运算符
显式的把对象转换为其他类型使用as
包括从可空类型转换到其非可空对应类型
如果转换不可能,程序会在 运行时崩溃。这就是它被称为 不安全 转换运算符的原因
fun main() {
val a: String? = null
// val b = a as String 程序运行时会崩溃
val b = a as? String //可以打印出null
println(b)
}
使用as?与Elvis运算符简化代码
//简化前
fun calculateTotalStringLength(items: List<Any>): Int {
var totalLength = 0
for (item in items) {
totalLength += if (item is String) {
item.length
} else {
0 // Add 0 for non-String items
}
}
return totalLength
}
//简化后
fun calculateTotalStringLength(items: List<Any>): Int {
return items.sumOf { (it as? String)?.length ?: 0 }
}
/**
说明:
- 对列表中的每个项目,使用 `String` 进行安全类型转换,通过 `as?`。
- 如果调用不返回 `null` 值,则使用安全调用 `?.` 来访问 `length` 属性。
- 如果安全调用返回 `null` 值,则使用 Elvis 运算符 `?:` 返回 `0`
**/
空值和集合
filterNotNull函数用来过滤null值
val emails: List<String?> = listOf("Bob@example.com", null, "Saul@example.com", null)
val validEmails = emails.filterNotNull()
println(validEmails)
listOfNotNull函数,在创建时就过滤
val serverConfig = mapOf(
"appConfig.json" to "App Configuration",
"dbConfig.json" to "Database Configuration"
)
val requestedFile = "appConfig.json"
val configFiles = listOfNotNull(serverConfig[requestedFile])
println(configFiles)
maxOrNull()查找最大值。如果不存在,则返回一个 null 值。
minOrNull()查找最小值。如果不存在,则返回一个 null 值
maxOrNull() 和 minOrNull() 函数设计用于与不包含 null 值的集合一起使用。否则,你无法判断函数是找不到期望的值,还是找到了一个 null 值
val temperatures = listOf(15, 18, 21, 21, 19, 17, 16)
val maxTemperature = temperatures.maxOrNull()
println("Highest temperature recorded: ${maxTemperature ?: "No data"}")
val minTemperature = temperatures.minOrNull()
println("Lowest temperature recorded: ${minTemperature ?: "No data"}")
singleOrNull
判断集合是否存在单个的目标元素,如果不存在或者存在多个都返回null
用于处理不包含null值的集合(推荐)
val temperatures = listOf(15, 18, 21, 21, 19, 17, 16)
val singleHotDay = temperatures.singleOrNull{ it == 30 }
println("Single hot day with 30 degrees: ${singleHotDay ?: "None"}")
firstNotNullOfOrNull
lambda表达式转换集合并返回第一个非null值
如果不存在则返回null值
data class User(val name: String?, val age: Int?)
fun main() {
val users = listOf(
User(null, 25),
User("Alice", null),
User("Bob", 30)
)
val firstNonNullName = users.firstNotNullOfOrNull { it.name }
println(firstNonNullName) //Alice
}
reduceOrNull
将集合中的元素通过指定的操作合并为单个结果,如果集合为空返回nullreduceOrNull() 函数设计用于不包含 null 值的集合(编译会报错)
// Prices of items in a shopping cart
val itemPrices = listOf(20, 35, 15, 40, 10)
// Calculate the total price using the reduceOrNull() function
val totalPrice = itemPrices.reduceOrNull { runningTotal, price -> runningTotal + price }
println("Total price of items in the cart: ${totalPrice ?: "No items"}")
// Total price of items in the cart: 120
val emptyCart = listOf<Int>()
val emptyTotalPrice = emptyCart.reduceOrNull { runningTotal, price -> runningTotal + price }
println("Total price of items in the empty cart: ${emptyTotalPrice ?: "No items"}")
// Total price of items in the empty cart: No items
提前返回与Elvis运算符结合
//return与Elvis版本可读性更高
fun getNumberOfFriends(users: Map<Int, User>, userId: Int): Int {
val user = users[userId] ?: return -1
return user.friends.size
}
fun getNumberOfFriends(users: Map<Int, User>, userId: Int): Int {
return users[userId]?.friends?.size ?: -1
}
库和API
标准库
大部分可以直接在Kotlin文件中使用,部分需要导入
标准库已经提供的类、函数、属性列表:
Kotlin库
Klibs.io
选择使用API
有些API被定义为需要选择才能使用,比如一些正在开发中的API
要启用,请编写 @OptIn 后跟包含分类 API 的类名的括号,然后附加两个冒号 :: 和 class
以标准库中的UIntArray函数为例
@ExperimentalUnsignedTypes
inline fun uintArrayOf(vararg elements: UInt): UIntArray
@OptIn(ExperimentalUnsignedTypes::class)
fun main() {
// Create an unsigned integer array
val unsignedArray: UIntArray = uintArrayOf(1u, 2u, 3u, 4u, 5u)
// Modify an element
unsignedArray[2] = 42u
println("Updated array: ${unsignedArray.joinToString()}")
// Updated array: 1, 2, 42, 4, 5
}
更多推荐


所有评论(0)