官方定义
如图1、图2所示,kotlin官方网站是这样定义这门编程语言的:让开发人员更快乐的一门编程语言。现代、简洁、安全的编程语言。
特点
官方自诩第一大特点:简洁
/*
使用一行代码创建一个包含 getters、 setters、 `equals()`、 `hashCode()`、 `toString()` 以及 `copy()` 的 POJO:
*/
data class Customer(val name: String, val email: String, val company: String)
官方自诩第二大特点: 安全
/**
* 彻底告别那些烦人的 NullPointerException——著名的十亿美金的错误
**/
var output: String
output = null // 编译错误
// Kotlin 可以保护你避免对可空类型进行误操作
val name: String? = null // 可空类型
println(name.length()) // 编译错误
官方自诩第三大特点:互操作性
/**
* 使用 JVM 上的任何现有库,因为有 100% 的兼容性,包括 SAM 支持。
**/
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
Flowable
.fromCallable {
Thread.sleep(1000) // 模仿高开销的计算
"Done"
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(::println, Throwable::printStackTrace)
// 无论是面向 JVM 还是 JavaScript 平台,都可用 Kotlin 写代码然后部署到你想要的地方
import kotlin.browser.window
fun onLoad() {
window.document.body!!.innerHTML += "<br/>Hello, Kotlin!"
}
补充特点:跨平台开发
kotlin可以用于服务器端开发、Android开发、JavaScript开发、原生开发。
协程
官方:异步或非阻塞程序设计是新的现实。
协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的。
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
print("World!") // 在延迟后打印输出
}
print("Hello,") // 协程已在等待时主线程还在继续
Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}
打印结果:Hello,World!
runBlocking协程构建器
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L)
println("World!")
}
println("Hello,") // 主线程中的代码会立即执行
runBlocking { // 但是这个表达式阻塞了主线程
delay(2000L) // ……我们延迟 2 秒来保证 JVM 的存活
}
}
调用了 runBlocking 的主线程会一直阻塞直到 runBlocking 内部的协程执行完毕。
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> { // 开始执行主协程
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L)
println("World!")
}
println("Hello,") // 主协程在这里会立即执行
delay(2000L) // 延迟 2 秒来保证 JVM 存活
}
使用 runBlocking 来包装 main 函数的执行。
轻量
import kotlinx.coroutines.*
fun main() = runBlocking {
repeat(10000) { // 启动大量的协程
launch {
delay(5000L)
print(".")
}
}
}
它启动了 1 万个协程,并且在 5 秒钟后,每个协程都输出一个点。
简单知识点分享
变量:
声明变量: var & val , 可变与不可变变量。
语法
- 创建对象不再需要new,如:var list = ArrayList()
- 代码结尾不再需要分号 “;“
方法:
fun method (num: Int) : Int { ... }
?与!!的区别:
?表示为空则跳过,相当于:
if (obj != null) {
//执行代码
} else {
return
}
!!表示如果为空则抛出异常,相当于:
if (obj != null) {
//执行代码
} else {
throw Exception
}
for循环:
for (str in list) { ... }
for (i int i..5) { ... }
for (i int 1..10 step 2) { ... }
for (i int 9 downTo 0 step 3) { ... }
when代替switch:
when (obj) :
1 -> { ... }
2 -> { ... }
作用域函数
with、run、let、apply、also
with
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) { // 画一个 100 像素的正方形
penDown()
for (i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}
with 可以理解为“对于这个对象,执行以下操作。”
run
fun main() {
val str = "Hello"
// this
str.run {
println("The receiver string length: $length")
//println("The receiver string length: ${this.length}") // 和上句效果相同
}
}
run和with做同样的事情,但是调用方式不一样。with是独立函数,run是扩展函数。
apply
fun main() {
val adam = Person("Adam").apply {
age = 20 // 和 this.age = 20 或者 adam.age = 20 一样
city = "London"
}
println(adam)
var list = ArrayList<String>().apply {
add("abc")
add("tom")
add("jack")
}
print(list)
}
- with 、run以及 apply 通过关键字 this 引用上下文对象;
- 调用某对象的apply函数,在函数范围内,可以调用该对象的任意public方法;run和apply类似,但是run返回的是最后一行,而apply是返回本身。
let & also
let 及 also 将上下文对象作为 lambda 表达式参数:
fun main() {
// it
str.let {
println("The receiver string's length is ${it.length}")
}
}
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
可以为上下文对象指定在作用域内的自定义名称,如下:
fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
作用域函数的典型用法及与java的区别:
Java
Person alice = new Person("Alice", 20);
println(alice);
alice.moveTo("London");
alice.incrementAge();
println(alice);
Kotlin
Person("Alice", 20).let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
类、类的继承与接口
- 类的声明用class
class Person {
...
}
- 如果一个类没有类体,花括号也可以省略:
class Person
- Kotlin 中非抽象类是不能被直接继承的,要使一个类可继承,需要用open关键字标记它:
open class Person
- 继承的时候用冒号,不再是java的extends:
class Student : Person()
- 一个类或者对象可以实现一个或多个接口:
interface Itest {
fun bar()
}
class Person: Itest {
override fun bar() {
// 方法体
}
}
- 接口可以有默认实现,但是不需要用default修饰:
interface Itest {
fun foo(){
print("interface a")
}
fun bar()
}
- 接口多实现
interface ItestA {
fun foo(){
print("interface A")
}
}
interface ItestB {
fun foo(){
print("interface B")
}
}
class Student : ItestA, ItestB {
override fun foo() {
super<Itest1>.foo()
super<Itest2>.foo()
}
}
静态变量与静态方法
- 将其包含在companion object域中:
class Constant {
companion object {
const val BASE_URL = "http://xxx" //静态常量, const 修饰符将其标记为编译期常量
fun foo() { /*...*/ } //静态方法
}
}
- 或者用object声明:
object test {
const val BASE_URL = "http://xxx"
fun foo() { /*...*/ }
}
构造函数
- 一个类中可以有一个主构造函数和多个次构造函数,主构造函数是类头的一部分:它跟在类名(与可选的类型参数)后。
- 如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字
- 主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
class Person constructor(name: String) {
init {
var name = name
}
}
- 以上代码,Kotlin 有简洁的语法:
class Person (var name: String)
- 当一个类既有主构造函数又有次构造函数时,所有次构造函数都必须使用this关键字直接或间接的调用主构造函数
class Person (name: String) {
constructor(name: String, age: Int): this(name)
constructor(name: String, age: Int, sex: String): this(name, age)
}
官方文档
以上是简单分享,附上kotlin文档
kotlin中文官方文档