kotlin 的只言片语

85 阅读4分钟

优点

Kotlin 跨平台 技术旨在简化跨平台项目的开发。 它减少了在不同平台编写和维护相同代码所需的时间,同时保持了本地编程的灵活性和优势,使用 Kotlin 构建的应用程序崩溃的可能性减少了 20%。

正如官方所说,主要特点是灵活性,一些语法糖,同时提高代码的健壮性(这个和代码功底有关,好的代码性不于语言是什么)。灵活性意味着代码的可读性质有所下降,总体优点突出,所以kotlin 成为了Android 的第一语音

一,基础语法

1.函数

fun sum(a: Int, b: Int): Int {
    return a + b
}
或
fun sum(a: Int, b: Int) = a + b


fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

fun foo(a: Int = 0, b: String = "") { ... }

函数参数类型处于之后,这是和Java 的区别,并可转化为推导函数,

因为kotlin 具有推导功能,所以变量类型在kotlin 并不是必须的,这个和ts 语音很像,目前这种具备强类型和推导类型的语音,都有此特点。

其中Unit 类型相当于Java void 的,并可省略

可以指定参数默认值:可以自定义默认数值,比Java 的扩展性更好

2.变量声明

Kotlin 中,你可以使用关键字 val 或 var 来声明变量,接着是变量的名称。

val x: Int = 5
var x: Int = 5
val x = 5
val x by lazy {
   5
}
 
lateinit var s: String

val 声明既确定相当于Java 的final ,要求属性声明为val

var 正常生成变量,Java中声明变量的方式一样。

没有类型:类型推断

lazy延迟声明:其实是一种设计模式,用到执行,可以提升代码的执行效率,这意味着该变量一旦初始化后就不允许再被修改值了(基本类型是值不能被修改,对象类型是引用不能被修改)。{}内的操作就是返回唯一一次初始化的结果。

lateinit: 只能用来修饰类属性并且不能val(用法冲突),不能用来修饰局部变量,一定也会在后面某个合理的时机将该属性对象初始化的.

3. 惯用语法

val positives = list.filter { it > 0 }

过滤条件并返回符合条件的list

if ("john@example.com" in emailsList) { ... } if ("jane@example.com" !in emailsList) { ... }

检查集合中元素是否存在

println("Name $name")

字符串插值

fun describe(obj: Any): String =
    when (obj) {
        1          -> "One"
        "Hello"    -> "Greeting"
        is Long    -> "Long"
        !is String -> "Not a string"
        else       -> "Unknown"
    }

替代Java 的Switch,弱化的类型支持更多内容

val list = listOf("a", "b", "c")

只读list

val map = mapOf("a" to 1, "b" to 2, "c" to 3)

只读映射

println(map["key"]) map["key"] = value

访问映射

val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
    println(item)
}
for ((k, v) in map) { println("$k -> $v") }

相当于Java 的增强for 循环

for (i in 1..100) { ... } // 封闭区间:包括 100 
for (i in 1..<100) { ... } // 开放区间:不包括 100
for (x in 2..10 step 2) { ... } // 一次减少2
for (x in 10 downTo 1) { ... } // 倒叙访问
(1..10).forEach { ... } // 增强遍历

while 循环

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

支持Java 的lambda 表达式

val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
    .filter { it.startsWith("a") }
    .map { it.uppercase() }
    .forEach { println(it) }

支持类型转换

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // 在此分支中,`obj` 会自动转换为 `String`。
        return obj.length
    }

    // 在类型检查分支外部,`obj` 仍然是 `Any` 类型。
    return null
}

快速单利创建:可以看到koltin 的写法确实简洁很多

object Resource { val name = "Name" } // 饿汉式实现


class SingletonDemo private constructor() {// 懒汉式
  companion object {
   private var instance: SingletonDemo? = null
   get() { if (field == null) { 
   field = SingletonDemo() } 
   return field }
   fun get(): SingletonDemo{return instance!! } } }
   

// 线程安全的:Synchronized 变成了注解
class SingletonDemo private constructor() {
    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }
        @Synchronized
        fun get(): SingletonDemo{
            return instance!!
        }
    }

}

//kotlin实现  双重校验锁式
class SingletonDemo private constructor() {
    companion object {
        val instance: SingletonDemo by lazy {
        SingletonDemo() }
    }
}

If-not-null (非空)简写

val files = File("Test").listFiles() println(files?.size) // 如果文件不为空,则打印大小

映射可空值 If-not-null(非空)转换

val value = ... val mapped = value?.let { transformValue(it) } ?: defaultValue // 如果值或转换结果为 null,则返回 defaultValue。

If-not-null-else (非空否则)简写

val files = File("Test").listFiles() // 对于简单的备用值: println(files?.size ?: "empty") // 如果 `files` 为 null,则打印 "empty"。 // 要在代码块中计算更复杂的备用值,请使用 `run`。 val filesSize = files?.size ?: run { val someSize = getSomeSize() someSize * 2 } println(filesSize)

在一个对象实例上调用多个方法(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() }

配置对象的属性(apply)

val myRectangle = Rectangle().apply { length = 4 breadth = 5 color = 0xFAFAFA }

交换两个变量

var a = 1 var b = 2 a = b.also { b = a }

4 创建类和实例

class Shape // 使用关键字 `class`

class Rectangle(val height: Double, val length: Double) { val perimeter = (height + length) * 2 }//默认构造函数,其中参数在类声明中列出,将自动可用

open class Shape
class Rectangle(val height: Double, val length: Double) : Shape() { val perimeter = (height + length) * 2 }

//类之间的继承使用冒号(`:` )来声明。 类默认是 `final` 的;要使一个类可继承,需要将其标记为 `open`:

koltin 的继承和实现为都是使用: 号,Java 中实现和继承用了不同方式,kotlin 的接口和抽象写法却和Java一样

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    
    init {
        println("First initializer block that prints $name")
    }
    
    val secondProperty = "Second property: ${name.length}".also(::println)
    
    init {
        println("Second initializer block that prints ${name.length}")
    }
}

First property: hello First
initializer block that prints hello 
Second property: 5
Second initializer block that prints 5

在实例初始化期间,初始化块按照它们在类体中出现的顺序执行,与属性初始化器顺序执行

  • 次构造函数: 一个类有很多次构造函数,Java 中并没有主次之分,有默认函数
class Person(val pets: MutableList<Pet> = mutableListOf())  
  
class Pet {  
constructor(owner: Person) {  
owner.pets.add(this) // 将此宠物添加到其所有者的宠物列表中  
}  
}
  • 主次共存:如果一个类有一个主构造函数,那么每个次构造函数都需要向主构造函数进行委托。 这可以直接委托,也可以通过其他次构造函数间接委托。 委托给同一类的另一个构造函数可以使用 this 关键字来实现:
class Person(val name: String) {  
val children: MutableList<Person> = mutableListOf()  
constructor(name: String, parent: Person) : this(name) {  
parent.children.add(this)  
}  
}
  • init 代码块: 类似Java 的静态代码块
class Constructors {
    constructor(i: Int) {
        println("Constructor $i")
    }
      init {
        println("Init block")
    }
}

Init block 
Constructor 1
  • 属性定义规则:
var <propertyName>[: <属性类型>] [= <属性初始化值>] [<getter>] [<setter>]
getter 和 setter 都是可选的,根据情况可以适当的省略


class Rectangle(val width: Int, val height: Int) {
    val area: Int // 属性类型是可选的,因为它可以从 getter 的返回类型中推断出来
        get() = this.width * this.height
}
fun main() {
    val rectangle = Rectangle(3, 4)
    println("Width=${rectangle.width}, height=${rectangle.height}, area=${rectangle.area}")
}
Width=3, height=4, area=12
  • 数据类

Kotlin 中的数据类主要用于保存数据。 对于每个数据类,编译器会自动生成额外的成员函数,这些函数允许你将实例打印为可读输出、比较实例、复制实例等。 数据类使用 data 标记:类似Java 的bean 类

data class User(val name: String, val age: Int)
  • 密封类

密封 类和接口提供了对类层次结构的控制继承。
所有直接子类在编译时都是已知的。 其他子类不能出现在密封类定义的模块和包之外。 同样的逻辑也适用于密封接口及其实现:一旦包含密封接口的模块被编译,就不能创建新的实现。一个密封类本身总是一个 抽象类 ,因此不能直接实例化

5 返回与跳转

在 Kotlin 中任何表达式都可以用标签来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@fooBar@。 要为一个表达式加标签,我们只要在其前加标签即可。

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}
un foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // 从传入 run 的 lambda 表达式非局部返回
            print(it)
        }
    }
    print(" done with nested loop")
}
//sampleEnd

fun main() {
    foo()
}

标签限定的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。指定返回或者退出的节点更容易指定退出位置,在代码开发过程去体验强大之处

函数式编程

SAM,全名为 Single Abstract Method,意思为“单一抽象方法”。 它的用法和 JavaScript 中的匿名函数或高阶函数类似。 主要用于将函数当做一个方法的参数进行传递的场景,一个仅包含一个抽象方法的接口被称为函数式接口 ,或者单一抽象方法(SAM)接口。 函数式接口可以拥有多个非抽象成员,但只能有一个抽象成员。

fun interface IntPredicate {
   fun accept(i: Int): Boolean
}

val isEven = IntPredicate { it % 2 == 0 }

fun main() {
   println("Is 7 even? - ${isEven.accept(7)}")
}
  • 扩展函数/属性: 类似于js 的扩展函数
open class Shape
class Rectangle: Shape()

fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"

fun printClassName(s: Shape) {
    println(s.getName())
}

函数式编程(SAM)接口

一个仅包含一个抽象方法的接口被称为函数式接口 ,或者单一抽象方法(SAM)接口

fun interface IntPredicate { fun accept(i: Int): Boolean }

// 原始写法
// 创建类的实例 val isEven = object : IntPredicate { override fun accept(i: Int): Boolean { return i % 2 == 0 } }


// 使用 lambda 创建实例
val isEven = IntPredicate { it % 2 == 0 }

扩展(新的装饰)

Kotlin 提供了一种扩展类或接口功能的能力,而无需继承自该类或使用设计模式,比如 装饰者(Decorator)。 这是通过称为 扩展(extensions) 的特殊声明完成的。主要是为了方便点符号调用新的函数

需要注意的是扩展函数会根据具体声明类型调用,不会跟着继承等作用

open class Shape
class Rectangle: Shape()

fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"

fun printClassName(s: Shape) {
    println(s.getName())
}

printClassName(Rectangle())
  • 支持重载
  • 可接受空对象
  • 可为伴生对象生成

将扩展声明为成员

你可以在一个类内部为另一个类声明扩展。 在这样的扩展内部,有多个隐式接收者(implicit receivers) - 可以无需限定符访问其成员的对象。 在扩展中声明的类的实例被称为分发接收者(dispatch receiver) ,扩展方法的接收者类型的实例被称为扩展接收者(extension receiver)。

class Host(val hostname: String) {
    // 构造函数
    fun printHostname() { print(hostname) }  // 方法

}
//扩展接收者
class Connection(val host: Host, val port: Int) {
    // 构造函数
    fun printPort() { print(port) }  // 方法

    // Host类的扩展函数
    fun Host.printConnectionString() {// 多接受者
        printHostname()   // 调用Host类的printHostname()方法
        print(":")
        printPort()   // 调用Connection类的printPort()方法
    }

    fun connect() {
        /*...*/
        分发接收者
        host.printConnectionString()   // 调用扩展函数
    }
}

数据类

Kotlin 中的数据类主要用于保存数据。 对于每个数据类,编译器会自动生成额外的成员函数,这些函数允许你将实例打印为可读输出、比较实例、复制实例等。 数据类使用 data 标记:

data class User(val name: String, val age: Int)

委托

委托模式 已被证明是实现继承的一个良好替代方案,Kotlin 原生支持它,并且不需要任何样板代码。

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val base = BaseImpl(10)
    Derived(base).print()
}