什么是Kotlin
Kotlin 是一门现代的、静态类型的能够运行在JVM上的编程语言,它旨在提供更简洁、安全、高效的软件开发体验,同时保持与 Java 完美互操作性,其支持函数式编程风格,拥有清晰的语法、强大的类型推断以及空安全特性,使开发者能够更轻松地编写可维护和可扩展的代码。
在Kotlin官网顶部你就可以看到这样一句话:
让开发人员更快乐的一门现代编程语言
它不仅是Android开发的首选语言,也能够用于服务端开发、JavaScript开发、数据科学,乃至使用Jetpack Compose构建UI、或是借助Kotlin Multiplatform构建跨平台应用程序
变量
在Kotlin中,变量只能通过var
或val
关键字来进行定义,其中var
声明为可变变量,而val
则声明为只读变量,val
类型的变量只能够为其赋值一次
与Java不同的是,其数据类型的声明是后置的,在声明变量时指定类型不是必须的,如果没有显式定义,那么这是Kotlin会根据其值来进行自动推断
// Kotlin
var str1: String = "Hello World"
//自动推断为String
val str2 = "Hello Kotlin"
// Java
String str = "Hello World";
你可能注意到,第一个例子中语句的末尾少了些什么,这是因为Kotlin中语句无需以分号;
来结尾,如果你仍然想保持像Java中的风格也没有问题,只是IDEA会告诉你这个分号是冗余的
在拼接字符串时,Kotlin提供了字符串模板
来优雅的将变量合并到一个String中,或是加上一对大括号来调用其方法返回值进行拼接
而要打印参数至标准输出只需要使用println()
或print()
方法即可,无需使用Java中的System.out.println()
,不过IDEA仍然保留了快捷操作,你仍然可以通过sout
来快捷生成输出语句
// Kotlin
val str = "Kotlin"
println("Hello str and{str.hashCode()}")
// Java
String str = "Kotlin";
System.out.println("Hello " + str + "and" + str.hashCode());
类与方法
在Kotlin中方法使用fun
关键字进行声明,与变量类似,方法的参数以及返回值的数据类型也应后置,对于返回值而言没有显式声明的情况下也会进行自动推断
// Kotlin
fun sum1(x: Int, y: Int) {
//自动推断为Int
return x + y
}
fun sum1(x: Int, y: Int): Int {}
// Java
int sum1(int x, int y) {
return x + y;
}
在不添加任何修饰符声明的类拥有public
与final
的默认属性,这意味着它默认不可被继承,且父类中的方法不可被重写,如果想进行这些操作必须使用open
关键字在类前加以修饰
// kotlin
class Main1() {}
open class Main2 {}
// Java
public final class Main1 {}
public class Main2 {}
你或许发现了在上面Kotlin定义的类名后接上了与方法中放置参数列表使用的括号()
,其实它也确实可以传入一个或多个参数,这种特性称之为主构造函数
;与变量一样,也可以声明类的参数的数据类型、是否可变以及其初始值
但这里传入的参数只能给内部变量或者使用init
关键字声明的初始化方法中调用,而在其内部定义的方法中是无法直接使用的
// kotlin
class dataProcess(
name: String = "张三",
age: Int = "18",
email: String
) {
val data = "nameage email"
init {
println("name ageemail")
}
}
对于Java而言,在类的声明中传递参数是不被允许的,因此只能够通过构造方法来进行,而为构造函数的参数指定默认值也无法直接进行。
// Java
public class DataProcess {
public DataProcess(String name, int age, String email) {
this.data = name + " " + age + " " + email;
System.out.println(name + " " + age + " " + email);
}
}
你可能会觉得很奇怪,类要传参有什么用呢?在Java中所有的方法或是变量声明都需要包含在一个类中,而Kotlin不然,你大可以把多个类、方法和变量都写在同一级,又或者是直接在类中乃至于方法中声明一个新的类,这都是被允许的
// kotlin
class Hello() {
class World() {}
}
fun main() {
class Nice() {}
}
val num = 1
因此你可以在方法中调用一个同级类,并给他传入一些参数或是调用其内部方法
// kotlin
class NumOperator(name: String) {
init {
println("$name Kotlin")
}
fun sum(x: Int, y: Int): Int {
return x + y
}
}
fun main() {
val result = NumOperator("Hello").sum(1, 2)
}
在Kotlin中要实现类去继承另一个类或者是接口需要使用:
而非Java中的extends
与implements
,要注意被继承的这个类必须以open
关键字声明,而接口则不需要,不过如果要继承自接口,就不允许再使用主构造函数
的方式来为这个类传参了。
如果要重写父类中的方法只需要在方法签名之前冠以override
即可,与类一样,被重写的方法也必须是open
的,Kotlin中这些操作都可以放在一个文件中进行,而Java则必须分为多文件。
// kotlin
class Corgi : Dog() {
override fun bark() {}
}
open class Dog : Biologic {
open fun bark() {}
}
interface Biologic {}
// Java
// Biologic.java
interface Biologic {
}
// Dog.java
public class Dog implements Biologic {
public void bark() {
}
}
// Corgi.java
public class Corgi extends Dog {
@Override
public void bark() {
}
}
其它
在Kotlin中提供了一种新的条件控制语句when
,这与其它语言中的switch
语句有些类似
when
语句在执行时会将其参数与所有的分支条件进行顺序比较,直到某个分支满足条件,而第一个符合条件的分支的值即为整个表达式的值,而忽略其它分支,类似于if
语句,其每个分支内也可以是一个代码块
val x = 6
when (x) {
1 -> println("x equals 1")
in 1..10 -> println("x is in the range")
x.hashCode() -> println("x is hashCode")
else -> println("none of the above")
}
如果不为when
提供任何参数,它的分支条件应为布尔表达式;其次,在一些特殊情况之外,绝大多数情况下都应为when
指定else
,上例中的1..10
为区间表达式的写法,它与x >= 1 && x <= 10
作用一样,而in
关键字则用于检测一个值是否在一个区间内部
在Java中要定义一个POJO不仅要设置其变量,还需要为它们编写对应的getters
、 setters
、 equals()
、 hashCode()
、 toString()
等等方法,而Kotlin中只需要一行代码即可完成一个包含完整内容的POJO,即使用使用data
关键字进行修饰的数据类来完成
//Kotlin
data class Employee(
val name: String,
val age: Int,
val email: String,
val department: String
)
在Kotlin中声明的所有变量在默认情况下都是不允许传入null
的,除非你在其数据类型后接上?
为该变量声明可空,从而避免对空对象进行操作导致爆出如Java中的NullPointerException
,并且在不知道传入参数的数据类型的情况下可以为其指定为Any
类型,这样只要类型检查正确编译器就会根据其值进行自动推断
//kotlin
var str1: String
//str = null 非空变量赋值null, 编译错误
var str2: String?
str2 = null
//println(str2.length()) 非法操作null值, 编译错误
fun cal(obj: Any) {}
Kotlin还能够使用任何的Java库,且声称有100%的兼容性,这就是所谓与Java的互操作性
我们先在build.gradle.kts
中引入FastJSON的依赖
dependencies {
implementation("com.alibaba.fastjson2:fastjson2:2.0.39")
}
然后写个简单的JSON字符串转成JSON对象
import com.alibaba.fastjson2.JSON
fun main() {
val str = "{'id': 1, 'name': '张三', 'gender': 1}"
val obj = JSON.parse(str)
println(obj)
}
结果可以说是非常的完美,这种强大的互操作性也为开发者从Java无痛转至Kotlin做好了十足的准备
Kotlin中还有一个非常好用的语法叫做扩展
,它可以实现在无需对一个类或者接口进行继承的情况下为它拓展新的功能,而新的方法就像原始对象本来就存在的方法一样可以被直接调用,这种机制称之为扩展函数
,此外也允许你为一个类添加新的变量,这则是扩展属性
在声明一个扩展函数时,需要选择一个被扩展的类型(又称接收者类型
)作为他的前缀,自定义的函数名与类型前缀之间应以.
嵌合,被拓展的类型同样可以被声明为可空;在扩展函数中,可以使用this
关键字来在其内部使用接收者对象
只要声明了扩展函数,在其同级与同级之下的任意位置该方法都可以被调用,因此大多数情况拓展都在顶层进行定义,但如果想在所定义的包之外被调用,则需要在文件中导入该方法
fun String.expandBrackets(): String {
return "[$this]"
}
var name = "张三".expandBrackets()
扩展函数与一般方式声明的方法没有声明区别,同样也可以为其传参、引入泛型或是以访问修饰符加以修饰,而下面出现的类型MutableList
是Kotlin中的可进行写操作的List
实现,其默认实现是ArrayList
fun MutableList.swap(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
val list = mutableListOf("apple", "banana", "pear").swap(0, 2)
扩展并不能对所拓展的类进行真正的修改,定义一个拓展也没有在类中插入新成员,只不过可以通过该类型的量基于.
表达式去调用拓展的新函数,所以说拓展是静态解析
的
而拓展属性
也十分类似,不过由于拓展没有实际插入成员至原类中,因此它不允许存在初始化器,它的行为只能够通过显式声明的getter
/setter
方法来进行定义
val MutableList.lastIndex get() = size - 1
val myList = mutableListOf(1, 2, 3).lastIndex
参考资料:
https://book.kotlincn.net/
https://kotlinlang.org/docs/home.html