2023-02-21
Kotlin
官方文档 From Java To Kotlin, git 菜鸟教程-Kotlin 类和对象
基本语法
变量
Constants and Variables
Java
String name = "Amit Shekhar";
final String name = "Amit Shekhar";Kotlin
var name = "Amit Shekhar"
val name = "Amit Shekhar"
Assigning the null value
Java
String otherName;
otherName = null;Kotlin
var otherName : String?
otherName = nullList
kotlin 列表元素分为: arrayOf 为不可变, Mutable 可变.
// arrayOf 为不可变
var riversArray = arrayOf("Nile", "Amazon", "Yangtze")
// 使用 += 赋值操作创建了一个新的 riversArray,
// 复制了原始元素并添加了“Mississippi”
riversArray += "Mississippi"
println(riversArray.joinToString())
// Mutable 为可变
var mutableList = mutableListOf("")
Verify if value is null 避免空指针 (?.)
https://kotlinlang.org/docs/kotlin-tour-null-safety.html#nullable-types
Java
if (text != null) {
int length = text.length();
}Kotlin
text?.let {
val length = text.length
}
// or simply
val length = text?.length
/////////// null 默认值操作
val name: String? = // 可能为 null 的字符串
// 如果 name 不为 null,则使用 name,否则使用默认值 "Guest"
val result: String = name ?: "Guest"
/////////// ?.let 操作符
name?.let {
// 在这里,name 不为空时执行
println("Name is $it")
}
/////////// Elvis 操作符
val user: User? = getUser()
// 如果 user 或 user.email 为 null,则使用默认值 "unknown"
val email: String = user?.email ?: "unknown"
/////////// 确定不会为 `null`,你可以使用非空断言运算符(`!!`)
//当你使用 `!!` 运算符时,如果该值是 `null`,它将抛出一个 `NullPointerException`。
val str: String? = "Hello, World!"
val nonNullStr: String = str!! // 如果 str 为 null,则会抛出异常
数组与反射
// 对应 java 的 String[] strings = {"a", "b"}
val strings = arrayOf("a","b")
// 对应 java 的 Integer[]
val results = Array(3){ i-> 0}
// 对应 java 的 Class[] cls = {ServicePathPlan.class}
val paramType = Array<ServicePathPlan>::class.java
// 对应 java 的 Class[] cls = {Object.class}
val paramType = Array<Any>::class.java
lateinit 关键字
lateinit 关键字用来修饰一个类的非空成员变量,表示该成员变量的值在稍后合适的时机会初始化,例如:
class Test {
lateinit var name: String
fun test() {
if (::name.isInitialized) {
println("name is initialized")
}
println(name)
}
}在给 lateinit 修饰的成员变量赋值之前如果有代码试图访问该成员变量的值,则会直接抛出异常。在访问 lateinit 修饰的成员变量之前可以先用 isInitialized 来判断该成员变量是否已经赋值了。
类型转换 as
val obj: Any = "This is a string"
// 将 obj 强制转换为 String 类型
val str: String = obj as String
//null 可用
val str: String = obj as? String
Verify if value is NotNull OR NotEmpty
Java
String sampleString = "Shekhar";
if (!sampleString.isEmpty()) {
myTextView.setText(sampleString);
}
if(sampleString!=null && !sampleString.isEmpty()){
myTextView.setText(sampleString);
}Kotlin
var sampleString ="Shekhar"
if(sampleString.isNotEmpty()){ //the feature of kotlin extension function
myTextView.text=sampleString
}
if(!sampleString.isNullOrEmpty()){
myTextView.text=sampleString
}Concatenation(拼接) of strings
Java
String firstName = "Amit";
String lastName = "Shekhar";
String message = "My name is: " + firstName + " " + lastName;Kotlin
var firstName = "Amit"
var lastName = "Shekhar"
var message = "My name is: $firstName $lastName"文本行
Java
String text = "First Line\n" +
"Second Line\n" +
"Third Line";Kotlin
val text = """
|First Line
|Second Line
|Third Line
""".trimMargin()Substring
Java
String str = "Java to Kotlin Guide";
String substr = "";
//print java
substr = str.substring(0, 4);
System.out.println("substring = " + substr);
//print kotlin
substr = str.substring(8, 14);
System.out.println("substring = " + substr);Kotlin
var str = "Java to Kotlin Guide"
var substr = ""
//print java
substr = str.substring(0..3) //
println("substring $substr")
//print kotlin
substr = str.substring(8..13)
println("substring $substr")Bitwise Operators
Java
final int andResult = a & b;
final int orResult = a | b;
final int xorResult = a ^ b;
final int rightShift = a >> 2;
final int leftShift = a << 2;
final int unsignedRightShift = a >>> 2;Kotlin
val andResult = a and b
val orResult = a or b
val xorResult = a xor b
val rightShift = a shr 2
val leftShift = a shl 2
val unsignedRightShift = a ushr 2流程控制
IF 表达式
// 传统用法
var max = a
if (a < b) max = b
// 使用 else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 作为表达式
val max = if (a > b) a else b
//也可以把 IF 表达式的结果赋值给一个变量。
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
// 区间
fun main(args: Array<String>) {
val x = 5
val y = 9
if (x in 1..8) {
println("x 在区间内")
}
}
When 表达式
when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。
when 类似其他语言的 switch 操作符。其最简单的形式如下:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。
如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}我们也可以检测一个值在(in)或者不在(!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)或者不是(!is)一个特定类型的值。注意: 由于智能转换,你可以访问该类型的方法和属性而无需 任何额外的检测。
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}循环(For-loops)
Java
for (int i = 1; i <= 10 ; i++) { }
for (int i = 1; i < 10 ; i++) { }
for (int i = 10; i >= 0 ; i--) { }
for (int i = 1; i <= 10 ; i+=2) { }
for (int i = 10; i >= 0 ; i-=2) { }
for (String item : collection) { }
for (Map.Entry<String, String> entry: map.entrySet()) { }Kotlin
for (i in 1..10) { }
for (i in 1 until 10) { }
for (i in 10 downTo 0) { }
for (i in 1..10 step 2) { }
for (i in 10 downTo 0 step 2) { }
for (item in collection) { }
for ((key, value) in map) { }循环(for each)
Java
// Java 7 and below
for (Car car : cars) {
System.out.println(car.speed);
}
// Java 8+
cars.forEach(car -> System.out.println(car.speed));
// Java 7 and below
for (Car car : cars) {
if (car.speed > 100) {
System.out.println(car.speed);
}
}
// Java 8+
cars.stream().filter(car -> car.speed > 100).forEach(car -> System.out.println(car.speed));
cars.parallelStream().filter(car -> car.speed > 100).forEach(car -> System.out.println(car.speed));Kotlin
cars.forEach {
println(it.speed)
}
cars.filter { it.speed > 100 }
.forEach { println(it.speed)}
// kotlin 1.1+
cars.stream().filter { it.speed > 100 }.forEach { println(it.speed)}
cars.parallelStream().filter { it.speed > 100 }.forEach { println(it.speed)}异常处理
try {
// 尝试执行的代码块,可能会抛出异常
throwException()
} catch (e: ArithmeticException) {
// 处理ArithmeticException异常
println("捕获到算术异常:${e.message}")
} catch (e: Exception) {
// 处理其他类型的异常
println("捕获到异常:${e.message}")
} finally {
// 无论是否发生异常,都会执行的代码
println("最终块:执行清理工作")
}
**try close **
// 正确
FileInputStream("file.txt").use { /* ... */ }函数定义
// 函数定义使用关键字 fun,参数格式为:参数 : 类型
fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int
return a + b
}
// 若只有一行 可忽略 {}
fun sum(a: Int, b: Int) = a + b
// public 方法则必须明确写出返回类型
public fun sum(a: Int, b: Int): Int = a + b
//无返回值的函数(类似Java中的void): 在 Kotlin 中,`Unit` 是一个特殊的类型,用于表示“没有有意义的值返回”。它类似于 Java 中的 `void`。
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):
public fun printSum(a: Int, b: Int) {
print(a + b)
}
//函数的变长参数可以用 vararg 关键字进行标识
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array<String>) {
vars(1,2,3,4,5) // 输出12345
}
lambda 表达式
常用定义
// { 参数列表 -> 函数体 }
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
// 无参 无返回值
fun main(args: Array<String>) {
val greeting = { println("Hello!")}
//调用函数
greeting()
}
//如果Lambda表达式,并且该参数的类型可以从上下文中推断出来,则可以"it"来引用该参数
arrayOf(1,3,6,74).forEach { println(it) }
// 或者指定名称
arrayOf(1,3,6,74).forEach { ele ->println(ele) }
// 带参数和返回类型的Lambda
// 下面的程序具有一个lambda表达式,该表达式接受两个整数作为参数,并返回这两个整数的乘积。
fun main(args: Array<String>) {
val product = { a: Int, b: Int -> a * b }
val result = product(9, 3)
println(result)
}
//线程 Runnable
mRunnable = Runnable {
println("ha")
}匿名对象 + 方法覆盖
// 匿名对象 + 方法覆盖
val adapter = object: SimpleAdapter(this, listMap,R.layout.map_layers_list_item, from, to){
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
return view
}
}label@ 标签用法
wim.webContext.connectDeviceManager.ifPresent outer@{ cdm ->
cdm.getConnectedDevice().ifPresent inerer@{ connectedDevice ->
// 假设这里是你的逻辑
if (1==1) {
// 使用带有标签的 return 来跳出外层函数
return@inerer
}
}
return@outer
}集合操作
创建集合
// 创建一个可变列表
val mutableList = mutableListOf(1, 2, 3, 4, 5)
// 创建一个不可变列表
val immutableList = listOf("apple", "banana", "orange")
// 创建一个可变映射(键值对)
val mutableMap = mutableMapOf(1 to "one", 2 to "two", 3 to "three")
// 创建一个不可变映射
val immutableMap = mapOf("key1" to "value1", "key2" to "value2")
遍历集合
val numbers = listOf(1, 2, 3, 4, 5)
// 遍历列表
for (number in numbers) {
println(number)
}
// 使用 forEach 遍历列表
numbers.forEach { println(it) }
// 遍历映射
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
for ((key, value) in map) {
println("Key: $key, Value: $value")
}
// 带索引
list.forEachIndexed { index, item ->
println("索引 $index : $item")
}过滤和转换集合
val numbers = listOf(1, 2, 3, 4, 5)
// 过滤列表
val evenNumbers = numbers.filter { it % 2 == 0 }
println("Even numbers: $evenNumbers")
// 映射列表
val squaredNumbers = numbers.map { it * it }
println("Squared numbers: $squaredNumbers")
//过滤和收集
val filteredContent = content.filter { it.id.isNullOrBlank() }.toCollection(mutableListOf())
排序集合
val numbers = listOf(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5)
// 升序排序
val sortedNumbers = numbers.sorted()
println("Sorted numbers: $sortedNumbers")
// 自定义排序
val sortedByLength = numbers.sortedBy { it.toString().length }
println("Sorted by length: $sortedByLength")
其他集合操作
val numbers = listOf(1, 2, 3, 4, 5)
// 判断是否包含特定元素
val containsThree = numbers.contains(3)
println("Contains 3? $containsThree")
// 求和
val sum = numbers.sum()
println("Sum: $sum")
// 查找最大值和最小值
val max = numbers.maxOrNull()
val min = numbers.minOrNull()
println("Max: $max, Min: $min")
类和对象
普通类
class Runoob() {
// Kotlin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
// 主构造器
init {
println("name is $name")
}
//次构造器
//如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
constructor(parent: Person) {
parent.children.add(this)
}
//类属性
var name: String =...
var url: String = ...
var city: String = ...
fun foo() { print("Foo") } // 成员函数
}
数据类 (DTO)
类似java的 record
data class Developer(var name: String, var age: Int)
data class JsMessage(val topic: String, val payload: String) {
companion object{
fun fromString(msg: String): JsMessage {
val jsonObject: JSONObject = JSONObject(msg)
val topic = jsonObject.getString("topic")
val payload = jsonObject.getString("payload")
return JsMessage(topic, payload)
}
}
override fun toString(): String {
val jsonObject = JSONObject()
jsonObject.put("topic", topic)
jsonObject.put("payload", payload)
return jsonObject.toString();
}
}解构声明
为数据类生成的 component 函数 使它们可在解构声明中使用:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")
Generics (泛型)
Java
// Example #1
interface SomeInterface<T> {
void doSomething(T data);
}
class SomeClass implements SomeInterface<String> {
@Override
public void doSomething(String data) {
// some logic
}
}
// Example #2
interface SomeInterface<T extends Collection<?>> {
void doSomething(T data);
}
class SomeClass implements SomeInterface<List<String>> {
@Override
public void doSomething(List<String> data) {
// some logic
}
}Kotlin
interface SomeInterface<T> {
fun doSomething(data: T)
}
class SomeClass: SomeInterface<String> {
override fun doSomething(data: String) {
// some logic
}
}
interface SomeInterface<T: Collection<*>> {
fun doSomething(data: T)
}
class SomeClass: SomeInterface<List<String>> {
override fun doSomething(data: List<String>) {
// some logic
}
}Anonymous Class
Java
AsyncTask<Void, Void, Profile> task = new AsyncTask<Void, Void, Profile>() {
@Override
protected Profile doInBackground(Void... voids) {
// fetch profile from API or DB
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// do something
}
};Kotlin
val task = object : AsyncTask<Void, Void, Profile>() {
override fun doInBackground(vararg voids: Void): Profile? {
// fetch profile from API or DB
return null
}
override fun onPreExecute() {
super.onPreExecute()
// do something
}
}
//////// 比如 Runnable
val t = thread { object: Runnable{
public override fun run() {
println("Hello World! for thread")
}
}
}
t.start()枚举
Java
public enum Direction {
NORTH(1),
SOUTH(2),
WEST(3),
EAST(4);
int direction;
Direction(int direction) {
this.direction = direction;
}
public int getDirection() {
return direction;
}
}Kotlin
enum class Direction(val direction: Int) {
NORTH(1),
SOUTH(2),
WEST(3),
EAST(4);
}
// 可以理解为 枚举类中的 NORTH, SOUTH 元素 继承了Direction, 所以它们都有direction属性继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
Any 默认提供了三个函数, 注意:
equals()
hashCode()
toString()
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){// 基类
}
// 类似C++ 委托构造
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}扩展 (类似JS的原型链)
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
- 扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
- 扩展函数不能访问被扩展类的私有成员。这是因为扩展函数实际上是在类的外部定义的,它没有像类的成员函数那样的访问权限。
- 如果在多个地方定义了同名的扩展函数,Kotlin 会根据导入的情况和调用的上下文来确定使用哪一个扩展函数。这可能会导致一些意想不到的行为,所以在定义和使用扩展函数时要注意命名冲突的问题。
class User(var name:String)
/**扩展函数, 类似 js 的原型链?**/
fun User.Print(){
print("用户名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}组合对象 (companion object)
在 Kotlin 中,companion object 是一个对象声明,允许在类内部创建一个与类相关联的单例对象。它类似于 Java 中的静态成员,但在很多方面更加灵活和强大。
- 访问权限:可以访问类的私有成员。
- 命名:不需要额外命名,直接使用
companion object。 - 访问方式:可以通过类名直接访问其内部的
companion object成员,而不需要实例化类。 - 继承:不能继承自其他类或对象,因为它本身就是一个单例对象。
class MyClass {
companion object {
// 在这里定义的成员可以通过类名直接访问
fun myFunction() {
println("This is a function inside companion object")
}
}
}// MyClass.myFunction() // 调用 companion object 中的函数
单例对象(single object)
Kotlin 中我们可以方便 使用 object 关键字 声明来获得一个单例。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ……
}
val allDataProviders: Collection<DataProvider>
get() = // ……
}
协程(Coroutines)
Kotlin 中的协程提供了一种全新处理并发的方式,可以在 Android 平台上使用它来简化异步执行的代码。协程是从 Kotlin 1.3 版本开始引入,但这一概念在编程世界诞生的黎明之际就有了,最早使用协程的编程语言可以追溯到 1967 年的 Simula 语言。
在 Android 平台上,协程主要用来解决两个问题:
- 处理耗时任务 (Long running tasks),这种任务常常会阻塞住主线程;
- 保证主线程安全 (Main-safety) ,即确保安全地从主线程调用任何 suspend 函数。 https://developer.android.google.cn/kotlin/coroutines?hl=zh_cn
dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
// 协程Java8支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3"
// lifecycle对于协程的扩展封装
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}创建协程
协程需要在一个作用域内启动。常见的作用域包括:
作用域
GlobalScope:全局作用域,不推荐用于大多数情况,因为它不会自动取消,可能导致内存泄漏。viewModelScope:在ViewModel中使用,当ViewModel被清除时,所有协程都会自动取消。lifecycleScope:在Activity或Fragment中使用,与生命周期绑定,当生命周期结束时,所有协程会自动取消。CoroutineScope:自定义作用域,适用于特定场景。
GlobalScope.launch/async{} - 全局启动的协程存在启动协程后的组件若被销毁, 但协程还存在的情况,极限情况下可能导致资源耗尽,因此并不推荐这样启动
viewModelScope
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login(username: String, token: String) {
// Create a new coroutine to move the execution off the UI thread
viewModelScope.launch(Dispatchers.IO) {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
loginRepository.makeLoginRequest(jsonBody)
}
}
}viewModelScope 是预定义的 CoroutineScope,包含在 ViewModel KTX 扩展中。请注意,所有协程都必须在一个作用域内运行。一个 CoroutineScope 管理一个或多个相关的协程。 launch 是一个函数,用于创建协程并将其函数主体的执行分派给相应的调度程序。 Dispatchers.IO 指示此协程应在为 I/O 操作预留的线程上执行。
调度器(Coroutine Dispatcher)
协程可以在不同的线程上执行,常见的调度器包括:
Dispatchers.Main:在主线程(UI 线程)上执行,适合更新 UI。Dispatchers.IO:在 I/O 线程上执行,适合处理 I/O 操作(如网络请求、文件读写、数据库操作)。Dispatchers.Default:在默认线程池上执行,适合 CPU 密集型任务。Dispatchers.Unconfined:不受限调度器,通常不推荐使用。
suspend 关键字
挂起函数是一种可以被暂停和恢复的函数,它使用 suspend 关键字进行声明。
它们不会阻塞线程, 只是给使用者的提醒:我是一个耗时函数, 我被我的创建者用挂起的方式放在后台运行,所以请在协程里调用我。
挂起的操作 —— 也就是切线程,依赖的是挂起函数里面的实际代码,而不是这个关键字。
所以这个关键字,只是一个声明。
withContext
如果只是使用 launch 函数,协程并不能比线程做更多的事。不过协程中却有一个很实用的函数:withContext 。这个函数可以切换到指定的线程(调度器),并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行。
CoroutineScope(Dispatchers.Main).launch { // 👈 在 UI 线程开始
val image = withContext(Dispatchers.IO) { // 👈 切换到 IO 线程,并在执行完成后切回 UI 线程
getImage(imageId) // 👈 将会运行在 IO 线程
}
avatarIv.setImageBitmap(image) // 👈 回到 UI 线程更新 UI
}
private suspend fun syncMapLayersConfig(){
// 从配置中拿
var localLayersConfig = withContext(Dispatchers.IO){ getLocalLayersConfig() }
setMapLayersConfig(localLayersConfig )
}委托
https://www.runoob.com/kotlin/kotlin-delegated.html
委托模式 (简而言之, 就是代理模式) 是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。
属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
val/var <属性名>: <类型> by <表达式>
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}