# kotlin

By [jimi.feng](https://paragraph.com/@mobilesuit) · 2022-08-05

---

本页面由袁琴兰于2022/03/30迁移自wiki 冯景珉空间

背景

*   在我之前的工作经历中会有用kotlin单独开发和用java kotlin混编的后端项目，这篇分享主要是列举kotlin对比java的一些优势 kotlin的优缺点
    
*   kotlin的优点
    
    *   语法简洁，后发优势，安全健壮，idea支持把java代码转换为kotlin代码
        
    *   支持多平台：jvm、native
        
    *   与java完全兼容，并且兼容java生态（spring、vert.x等）
        
    *   协程 kotlin coroutine
        
*   kotlin的缺点
    
    *   在同一个module下和lombok不兼容
        
    *   于java交互需要注意空安全
        
    *   编译速度慢
        
*   如果对kotlin的性能存在顾虑，可以参考 Kotlin's hidden costs - Benchmarks
    
*   kotlin文档传送门kotlin官方文档，kotlin文档中文地址 基础部分 类型推导 kotlin通过var和val声明变量，并且支持类型的推导(1.4以后支持联合类型推导) val str = "123" // 识别为字符串类型 val num = if(isFloat()) 1F else 1L //在1.3中，这个if表达式的类型会被识别为Any（kotlin所有对象的父类），但是在1.4版本中，这个if表达式会被识别为Number和Comparable类型 字符串处理 kotlin提供了更好用的字符串模板
    
*   java style String s = "abc"; String template = "%s.length is %d"; String str = String.format(template, s, s.length());
    
*   kotlin style val s = "abc" val str = "$s.length is ${s.length}" 相等判断 kotlin明确了引用比较和值比较，并对操作符进行重载（kotlin中的==默认就是调用对象的equals方法，包括字符串比较也可以直接使用==）
    
*   java style public static void main(String\[\] args) { String s1 = new String("123"); String s2 = new String("123"); System.out.println(s1 == s2);//输出false System.out.println(s1.equals(s2));//输出true }
    
*   kotlin style fun main() { val s1 = java.lang.String("123") val s2 = java.lang.String("123") println(s1 === s2)//输出false println(s1 == s2)//输出true } if、when表达式 kotlin的if和when(对应switch case)都能转换为表达式
    
*   java style public static void main(String\[\] args) { User user; if (flag) { user = findUserV1(); } else { user = findUserV2(); } }
    

public static void main(String\[\] args) { User user; switch (flag) { case true: user = findUserV1(); break; case false: user = findUserV2(); break; default:break;} }

*   kotlin style enum class Version { V1, V2 }
    

fun main() { val flag: Version = ... val user = if (flag == Version.V1) { findUserV1() } else { findUserV2() } }

fun main() { val flag: Version = ... val user = when (flag) { Version.V1 -> findUserV1() Version.V2 -> findUserV2() } }

fun main() { val flag: Version =... // when也可以不带条件参数 val user = when { flag == Version.V1 -> findUserV1() flag == Version.V2 -> findUserV2() else -> Any() } } 空安全 java的空指针真是老生常谈了，虽然java8补足了Optional，但是个人感觉Optional的使用度依然不高，kotlin直接从语法层面，规避了这个问题，kotlin必需区分对象的类型为可空还是非空，你使用可空类型(即在类型后面加上?，比如String?)，那么当你在处理的时候必需通过?操作符号进行调用，比如： //假设定义了a和b两个字符串对象，a表示非空字符串，b表示可为空字符串 var a: String = "abc" var b: String? = "abc"

//赋null值 a = null //编译报错，不能给非空对象赋空值 b = null //正常编译

//调用方法 val aLength = a.length //正常编译 val bLength = b.length //编译报错 val bLength = b?.length//正常编译，如果b为空，那么获取到null val bLength = b?.length ?: 1 //正常编译，如果b为空，那么获取到值为1

*   java style //常见的java空判断，如果仅仅想获取一个对象比较内层的属性，需要大量的if判空操作 A a = ...; C c = null; if(a != null) { B b = a.getB(); if (b != null) { c = b.getC() } }
    
*   kotlin style //如果a或者b为空，那么链路会直接返回null val c = a?.getB()?.getC() 由于java本没有非空类型，所以一般在项目中存在java kotlin混编的情况下，最佳实践是在java方法入参追加@Nullable and @NotNull两个注解进行修饰，这样kotlin在调用时可以识别入参是否为可空和非空类型 类型转换 在使用java的时候，我们总是不可避免的要对对象进行类型判断，但是通过instanceof判断之后，我们还必须手动进行一次强制类型转换，这样难免感觉多此一举（jdk14中支持了instance模式匹配）
    
*   java style public static void main(String\[\] args) { Object user = new User(1); if (user instanceof User) { int id = ((User) user).getId(); } }
    
*   kotlin style fun main() { val user: Any = User(1) if (user is User) { //通过is判断之后，编译器会自动识别对象类型，无需手动再类型转换一次 val id = [user.id](http://user.id) } }
    

fun main() { val user: Any = User(1) //as等效于类型强制转换 val id = (user as User).id }

fun main() { val user: Any = Any() //某使用as?进行更安全的类型强制转换，如果类型不匹配，那么会拿到一个null val id = (user as? User)?.id } 类型别名 kotlin的类型别名为现有类型提供替代名称。 如果类型名称太长，你可以另外引入较短的名称，并使用新的名称替代原类型名。 import java.util.function.Function

typealias CharSet = HashSet typealias JavaContainFilter = Function<K, Boolean> fun main() { val charSet = CharSet() for (char in 'a'..'g') { charSet.add(char) } println(charSet.joinToString()) // 输出a b c d e f g val containFilter = JavaContainFilter(String::isNotEmpty) println(containFilter.apply("")) // 输出false } 数据类 kotlin支持定义数据类，类似于jdk14中的record data class User(val name: String, val age: Int) fun main() { val jack = User("jack", 10) val (jackName, jackAge) = User("jack", 10) // 支持解构 println("$jackName , $jackAge") val jackCopy = jack.copy() // 支持浅拷贝 println(jack == jackCopy) // 输出true println(jack === jackCopy) // 输出false } kotlin高级特性 函数 kotlin中的函数是一等公民，函数可以在顶层定义而不一定在类中，函数也可以作为参数进行传递 fun infoLog(message: Array) { println("\[INFO ${Thread.currentThread().name}\] ${message.joinToString()}") } class Demo { fun infoLogDelegate(message: Array, func: (Array) -> Unit) { func(message) } } fun main() { val logFunction = ::infoLog logFunction(arrayOf("1", "2", "3")) // 输出1, 2, 3 logFunction.invoke(arrayOf("1", "2", "3")) // 输出1, 2, 3 //logFunction() 和 logFunction.invoke()是等效的 val logFunctionDemo = Demo::infoLogDelegate val demo = Demo() // kotlin的构造不需要new关键字 logFunctionDemo(demo, arrayOf("1", "2", "3"), logFunction) // 传递函数 // 自定义函数处理 logFunctionDemo(demo, arrayOf("1", "2", "3")) { println("\[INFO ${Thread.currentThread().name}\] ${it.joinToString()}") } } kotlin支持匿名函数，匿名函数只能作为返回值或者定义为属性 val func = fun(message: String) { println(message) } val funcV2 = { message: String -> println(message) } fun main() { func("test") funcV2("test") } 闭包 kotlin支持闭包 fun test(): () -> Unit { var a = 3 //状态 return { a++ println(a) } } fun main(args: Array) { val t = test() // 输出4, 5, 6 repeat(3) { t() } } 带接收者的lambda class HTML { // fun body() { TODO() } } fun html(init: HTML.() -> Unit): HTML { val html = HTML() // 创建接收者对象 html.init() // 将该接收者对象传给该 lambda return html } fun main() { html { // 带接收者的 lambda 由此开始 body() // 调用该接收者对象的一个方法 } } 扩展函数 和 扩展属性 kotlin可以对类型追加扩展函数, 比如我们可以在ArrayList上增加一个removeFirstSafe的方法 val ArrayList.lastIndex: Int get() = this.size - 1 fun ArrayList.removeFirstSafe(): T? = if (this.isNotEmpty()) { this.removeAt(0) } else { null } fun main() { val list = Lists.newArrayList(1, 2, 3) list.removeFirst() // list可以直接在对象上使用removeFirstSafe方法 println(list.joinToString()) // 输出2,3 println(list.lastIndex) // 输出1 } 中缀函数 kotlin可以在函数定义之前增加infix关键字，可以函数定义为中缀函数（中缀函数要求函数有且只有一个参数） //以kotlin内置函数为例 public infix fun <A, B> [A.to](http://A.to)(that: B): Pair<A, B> = Pair(this, that) //"1" to 1等价于 "1".to(1) val pair = "1" to 1 符号重载 kotlin可以对符号进行重载 一元运算符 表达式 函数名 +a unaryPlus -a unaryMinus !a not ++a, a++ inc --a, a-- dec 二元运算符 表达式 函数名 a += b , a = a + b plus(x) a -= b , a = a - b minus(x) a \*= b , a = a \* b tiems(x) a /= b , a = a / b div(x) a %= b , a = a % b rem(x) a .. b rangTo(x) 比较运算符 表达式 函数名 a == b a?.equals(b) ?: (b === null) a != b !(a?.equals(b) ?: (b === null)) a > b a.compareTo(b) > 0 a < b a.compareTo(b) < 0 a >= b a.compareTo(b) >= 0 a <= b a.compareTo(b) <= 0 符号重载eg (kotlin其实已经为集合类型提供了内置的符号重载了)： operator fun ArrayList.plus(item: T): ArrayList { this.add(item) return this } fun main() { val list = ArrayList() list + "1" + "2" println(list.joinToString()) // 输出1, 2 } 集合和序列 除了集合之外，Kotlin 标准库还包含另一种容器类型——序列（Sequence）。 序列提供与 Iterable 相同的函数，但实现另一种方法来进行多步骤集合处理。 当 Iterable 的处理包含多个步骤时，它们会优先执行：每个处理步骤完成并返回其结果——中间集合。 在此集合上执行以下步骤。反过来，序列的多步处理在可能的情况下会延迟执行：仅当请求整个处理链的结果时才进行实际计算。 操作执行的顺序也不同：Sequence 对每个元素逐个执行所有处理步骤。 反过来，Iterable 完成整个集合的每个步骤，然后进行下一步。 fun main() { val list = listOf(1, 2, 3, 4, 5) // 热数据流 var evenList = list.filter { println("filter $it") it and 1 == 0 }.map { println("map $it") it } // 冷数据流 var oddList = list.asSequence().filter { println("filter $it") it and 1 == 1 }.map { println("map $it") it }.toList() // 只有调用toList才执行 } // 和java中的stream不同，sequence是可以重复多次执行的 fun main() { val list = listOf(1, 2, 3, 4, 5) val sequence = list.asSequence().filter { println("filter $it") it and 1 == 1 } repeat(3) { sequence.map { println("map $it") it }.toList() } } 内联函数 kotlin支持通过在方法前增加inline关键字来将方法进行内联 public inline fun IntArray.forEach(action: (Int) -> Unit): Unit { for (element in this) action(element) } fun main() { val ints = intArrayOf(1, 2, 3, 4, 5) ints.forEach { println(it) } } java编译后泛型会被擦除，但是kotlin也可以inline结合reifeid关键字，获取泛型的真正类型 inline fun displayJavaClass(t: T) { println(T::class.java) } fun main() { displayJavaClass(1) // 输出class java.lang.Integer }

---

*Originally published on [jimi.feng](https://paragraph.com/@mobilesuit/kotlin)*
