Kotlin 入门指南
最接近的答案是Kotlin……
克拉西
var 和 val
空字符和 !!、?.、?:
函数
模式匹配(又称类型检查)
类型铸造
如果我们创造一种语言,将其他流行语言的优点都吸收进去,会怎么样呢?
最接近的答案是Kotlin……
Kotlin 目前越来越受欢迎。根据这份报告,Kotlin 是增长速度第四快的编程语言。
让我们一起来探究一下。
如果你之前用的是Java,Kotlin能让你编写更少的代码。它能去除所有冗余代码,让你的代码优雅简洁。
例如,您可以编写一个简单的“Hello World”程序,如下所示:
println("Hello World!!!")
如果你觉得它看起来像脚本语言,那么你的感觉很可能是对的。但主要区别在于 Kotlin 是一种静态类型编译型语言。当我们执行上面的println函数时,该函数会被编译并执行。
fun编写 Kotlin 函数是这样的:
fun hello() {
println("Hello World!!!")
}
没有分号 🎉
无需定义类 🎉💥
无需默认修饰符 🎉💥✨
上述函数是一个package-level函数。当我们将代码反编译成 Java 代码时,它将是final staticKotlin 根据文件名自动生成的类中的一个函数。
克拉西
太好了,我喜欢类,请问在 Kotlin 中如何定义类?
Kotlin 让它再次变得优雅简洁:
class Greeting
就是这样,这就是一个类定义,甚至不需要用花括号来限定作用域。编译器会处理一切。
Greeting 类final默认是一个类。如果你想知道为什么,请回想一下经典的《Effective Java》定义。
让我们在类内部定义函数。
class Greeting {
fun sayHello(): String {
return "Hello"
}
fun sayHelloWithName(name: String) = "Hello $name"
}
这sayHello是一个经典的函数,它返回一个StringHello 对象。请注意,我们使用 `{{ return type }}` 符号定义了返回类型: String。
第二个函数接受一个sayHelloWithName参数。由于它是一个单行函数,我们可以使用`&` 符号,后跟它的返回值。Kotlin 再次力求减少样板代码。nameString=
Kotlin 通过消除样板代码,让您享受编写代码的过程。但请记住,编译器会为您完成这些工作。这意味着 Kotlin 的编译时间会比 Java 更长。
构造函数
在面向对象编程中,类代表对象。对象拥有成员函数和构造函数来初始化自身。
例如,如果您想创建一个带有构造函数的员工类。
class Employee (name: String, salary: Long)
这被称为主构造函数。
我们可以初始化员工:
val employee = Employee("Raj", 100000L)
注意:没有
new用于初始化对象的关键字。
如果您想为一些入职时已具备一定经验的员工添加经验字段,该怎么办?我们将使用secondary constructors……
Kotlin 提供了一种使用关键字定义多个构造函数的方法constructor。
class Employee (name: String, salary: Long) {
constructor(name: String, salary: Long, experience: Int): this(name, salary)
}
我们使用关键字定义了辅助构造函数,constructor后跟输入参数列表。
注意:当定义了辅助构造函数和主构造函数时,我们应该将辅助构造函数委托给主构造函数。
我们不能在主构造函数中定义代码。这意味着,如果您想在初始化期间定义任何代码,则必须使用 ` constructorinit` 方法或 ` initset` 方法。
init 方法在类初始化时调用。
class Employee (name: String, salary: Long) {
init {
println("Employee ${this.name} is added")
}
}
初始化方法将在辅助构造函数初始化之前调用。
init一个类中可以包含多个方法。这些init方法会按照定义顺序被调用。
遗产
Kotlin 为继承提供了简便的语法。
class Manager {}
现在,Manager 类必须继承自 Employee 类。毕竟,他们都是员工,对吧。
class Manager: Employee() {}
运行上述代码时,编译器会报错。主要原因是 Employee 类有一个带两个参数的主构造函数。我们需要像下面这样将参数传递给 Employee 类。
class Manager: Employee(name: String, salary: Long)
但是等等,我们从哪里获取这些输入给父类呢?我们将通过 Manager 的主构造函数传递它们。
class Manager(name: String, salary: Long): Employee(name: String, salary: Long)
当然,经理应该有自己的队伍吧?我们也会让他们过关的。
class Manager(name: String, salary: Long, team: List<Employee>): Employee(name: String, salary: Long)
但编译器仍然报错。Kotlin 默认将所有类声明为 final。
如果你想继承某个类,你应该open使用open关键字。如下所示。
open class Employee(name: String, salary: Long)
class Manager(name: String, salary: Long, team: List<Employee>): Employee(name: String, salary: Long)
现在编译器很满意了。😃
Kotlin 还提供了
sealed classes更多信息,请点击此处阅读。
数据类
冗余冗长的代码Plain Old Java Objects需要休息一下。Kotlin 的数据类使代码简洁明了,并消除了所有样板代码。不需要 ` gettersand` 和` settersor`。
data class User(val name: String, val age: Int)
编译器会自动创建所有内容。
var 和 val
在 Kotlin 中,不可变类型是库内置的。你定义的类是 final 的,方法也是 final 和 static 的。
要定义一个不可变值,我们使用关键字val:
val message = "Hello"
一旦定义val,它就是不可更改的。
然而,Kotlin 作为一种友好的语言,也为您提供了var……
用 定义的值var是可变的。
var message = "Don't use me, it is a shame!!"
空字符和 !!、?.、?:
Kotlin 强制你编写空安全代码。它会在编译时尝试消除空指针异常 (NPE)。
Kotlin 会编译成 Java 代码,然后再编译成 JVM 字节码。Java 有空指针异常(Null PointerException),因此可能会出现 NPE 的情况。
var message = "Hello"
message = null
上述代码会出现编译错误,但null其他地方都正确。
在 Kotlin 中,我们可以使用 `Optional` 定义一个String?可以接受null值的 Optional 类型。
var message: String? ="Hello"
message = null
上面的代码可以编译。但是当你在其他地方使用上面的代码时,例如
message.toUpperCase()
编译器会发出警告。也就是说,Kotlin 编译器会在编译时阻止你调用可能为空的值。
我们可以使用以下任何一种语法来关闭编译器:
!!或?.?:
第一个括号!!称为not-null assertion operator“或” dirty operator。双感叹号告诉编译器:“闭嘴编译,我知道自己在做什么。”
message!!.toUpperCase()
这并不能防止运行时空指针异常。
这?.被称为Safe-call operator。此运算符告诉编译器,如果值不为空,则调用该函数。
message?.toUpperCase()
这?.是糖语法
if (message != null) {
message.toUpperCase()
}
它?:被称为elvis operator,它告诉编译器当不为空时执行此操作,否则执行另一操作。
message.toUpperCase() ?: "GIVE ME MESSAGE"
这?:是糖语法
if (message != null) {
message.toUpperCase()
} else {
"GIVE ME MESSAGE"
}
函数
lambda
Kotlin 还允许在需要时轻松切换到函数式编程环境。在 Kotlin 中,函数是同等重要的组成部分。
这意味着什么
first-class functions?这些函数可以用作表达式或数据结构,也可以通过参数和返回类型传递。
val evenNumbers = 0..100.toList().filter { number -> number % 2 == 0 }
0..100.toList()创建一个包含 0 到 100 的列表。然后我们应用filter函数(我们不需要对stream它们进行操作)。
过滤器函数接受一个 lambda 函数作为参数。我们可以同时使用这两种()语法{}。
然后,在 lambda 函数内部,我们检查是否为偶数并获取结果。最终返回的值是一个数字列表(你不需要处理collect这些数字)。
Kotlin 的确it简洁明了。上面的数字是多余的,我们可以用 `.` 去掉它it。
val evenNumbers = 0..100.toList().filter { it % 2 == 0 }
该值it代表列表中的当前值。
我们可以将不同的函数串联起来,进行进一步的变换。
虽然 Java 代码比较冗长,但 Kotlin 代码却简洁明了。
扩大
Kotlin 的一个突出特点是extension函数。假设你有一个类型Color,它接收一个十六进制字符串并将其转换为 RGB 值。你可以extension对一些基本类型(例如 `String`)创建类似下面的函数String。
fun String.rgb() = this.removePrefix("#").windowed(2, 2).map { Integer.valueOf(it, 16) } // returns a list
println("#FFFFFF".rgb()) // 255, 255, 255
那么这里会发生什么呢?
我们定义一个rgb扩展函数并将其附加到String基类。该扩展函数包含this一个参数,用于表示我们调用rgb方法所针对的值。然后,我们使用函数移除前缀removePrefix。移除前缀后,我们将字符串截取为 2 个字符的块,并将结果中的十六进制字符串转换为整数。最后,返回一个数字列表。
这段代码很优雅,而且作为字符串中定义的函数也十分自然。
注意:我们还可以使用函数覆盖现有功能
extension。因此,请谨慎使用。
排队
我们可以inline在函数定义中添加关键字。这将使函数在调用点处以内联形式运行。
虽然内联可以提高应用程序的性能,但要注意,内联的函数越多,内存开销就越大。
模式匹配(又称类型检查)
我第一次接触到模式匹配时,Rust简直惊呆了。我一直都希望能在 JVM 中实现这个功能。但当时我既找不到也不知道有任何相关的实现。
使用 Kotlin,我们可以通过模式匹配来检查类型。这大大减少了类型转换和样板代码。无需手动转换对象,编译器会更高效地完成这项工作。
when (message) {
is Int -> println("$message is a number")
is String -> println("$message is a string")
is Any -> println("${message} is any")
}
那么这里发生了什么呢?is关键字用于模式匹配,它匹配消息类型。
当消息类型为 时Int,Int调用该段。
当消息类型为 时String,String调用该段。
当消息类型为 时Any,Any调用该段。
这项功能即将在 Java 中推出——请查看我关于 Java 13 的文章(链接在此)。
类型铸造
我们可以使用关键字来转换对象的类型as。
我们可以将一种类型转换为另一种类型,如下所示:
(1..10).toList().map { it as Any }
Any`[Any]` 是基类型,它被定义为 Kotlin 类层次结构的根。每个 Kotlin 类都以 `[Any]` 作为超类。
所以我们正在使用关键字Integer进行类型转换。Anyas
同样,我们也可以使用类型转换as?,这将提供safe-nullable类型转换。
点击此处查看这篇关于如何使用 KHipster 生成 Kotlin、React 和 Spring Boot 全栈应用程序的文章。
你可以在推特上关注我。
如果你喜欢这篇文章,请点赞或留言。❤️
文章来源:https://dev.to/sendilkumarn/kotlin-for-developers-an-introduction-2kjg