kotlin 官方doc学习笔记

Get started with Kotlin

Kotlin is a modern but already mature programming language aimed to make developers happier. It’s concise, safe, interoperable with Java and other languages, and provides many ways to reuse code between multiple platforms for productive programming.
kotlin是javm语言,所以天然的可以使用java的巨大生态,这一点灰常重要。可以把kotlin视为better java,提供了各种语法糖来减少编写的代码数量,更容易使用kotlin实现函数式编程,声明式编程。

Use Kotlin and third-party libraries in your application. Learn more about adding library and tool dependencies to your project.

The Kotlin standard library offers a lot of useful things such as collections or coroutines.

Take a look at the following third-party frameworks, libs and tools for Kotlin.

kotlin提供的集合类型比java好用太多,另外scope function: apply, also, let让代码更简洁,减少了很多临时变量的使用

第三方库:对于后端开发来说,直接使用spring的kotlin版本就OK,注意Mockk是推荐的mocking library

Kotlin for server side

Expressiveness: Kotlin’s innovative language features, such as its support for type-safe builders and delegated properties, help build powerful and easy-to-use abstractions.

Scalability: Kotlin’s support for coroutines helps build server-side applications that scale to massive numbers of clients with modest hardware requirements.

Interoperability: Kotlin is fully compatible with all Java-based frameworks, so you can use your familiar technology stack while reaping the benefits of a more modern language.

Migration: Kotlin supports gradual migration of large codebases from Java to Kotlin. You can start writing new code in Kotlin while keeping older parts of your system in Java.

Tooling: In addition to great IDE support in general, Kotlin offers framework-specific tooling (for example, for Spring) in the plugin for IntelliJ IDEA Ultimate.

Learning Curve: For a Java developer, getting started with Kotlin is very easy. The automated Java-to-Kotlin converter included in the Kotlin plugin helps with the first steps. Kotlin Koans can guide you through the key features of the language with a series of interactive exercises.

就是总结了kotlin相对于java的优势

Basic syntax

fun main() {
    println("Hello world!")
}

kotlin并不是pure OOP的,它可以单独声明func , object, val等全局函数,对象,变量

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

可以使用Unit表示返回空值,这一点挺方便的,比如在mock一个没有返回值的函数时

every { mockObject.noReturnValueFunc() } returns Unit

关于变量

fun main() {

    val a: Int = 1  // immediate assignment
    val b = 2   // `Int` type is inferred
    val c: Int  
    if (b < 4) {
        c = 4
    }else {
        c = 3
    }

    println("a = $a, b = $b, c = $c")
}

最有用的一点是, 不可变的变量可以延迟赋值,在java里面const变量必须在声明的时候赋值

String templates

val s2 = "${s1.replace("is", "was")}, but now is $a"

这个真的是超级有用,比java的String.fmt好用多了,直接在string里面插入变量或者表达式

Conditional expressions

fun maxOf(a: Int, b: Int) = if (a > b) a else b

在kotlin里面,所有的表达式都是有返回值的,返回空的函数可以认为是返回Unit。
比如这个if-else的返回值,就省掉了声明一个变量然后在各个分支赋值一遍的写法。

Ranges and progressions.

val x = 10
val y = 9
if (x in 1..y+1) {
    println("fits in range")
}

if (-1 !in 0..list.lastIndex) {
    println("-1 is out of range")
}

in !in, step
这些方法在迭代纯数字的时候很有用

Collections
集合可以说是最好用的工具了

when {
    "orange" in items -> println("juicy")
    "apple" in items -> println("apple is fine too")
}

fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.uppercase() }
    .forEach { println(it) }

Type checks and automatic casts

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` is automatically cast to `String` in this branch
        return obj.length
    }

    // `obj` is still of type `Any` outside of the type-checked branch
    return null
}

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null

    // `obj` is automatically cast to `String` in this branch
    return obj.length
}

在有类型判断的情况下,kotlin能自动推断出接下来的代码里变量是什么类型,省掉了很多不安全的强制类型转换。

Idioms

https://kotlinlang.org/docs/idioms.html
kotlin常见用法,真的是精髓

data class Customer(val name: String, val email: String)
val (v1, v2) = customer

自动expand也很牛逼

Lazy property

val p: String by lazy { // the value is computed only on first access
    // compute the string
}

在kotlin里面实现单例模式真的太简单了

Instantiate an abstract class

abstract class MyAbstractClass {
    abstract fun doSomething()
    abstract fun sleep()
}

fun main() {
    val myObject = object : MyAbstractClass() {
        override fun doSomething() {
            // ...
        }

        override fun sleep() { // ...
        }
    }
    myObject.doSomething()
}

类似于java的匿名类的使用

Java 7’s try-with-resources

val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
    println(reader.readText())
}

kotlin use可以自动关闭closeable object

Mark code as incomplete (TODO)

fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")

这才是标志某个函数没有实现的正确方法

扩展函数:

package extendbean

//本包中可以不同导入 Student 包
//import extendbean.Student

//在本包内定义的扩展函数就不需要导入包了
//import extendbean.printAge
//import extendbean.printName

//使用 Student 的扩展 , 也需要导入 扩展函数所在包名.扩展函数名
import extenddeclare.printNameAndAge

fun main() {
    var student : Student = Student()
    student.printNameAndAge()
    student.printName()
}

扩展函数的背后原理
扩展函数实际上就是一个对应 Java 中的静态函数,这样在外部感觉和使用类的成员函数是一样的.其实就是一个语法糖,它并没有破坏或者说修改原有的类

扩展函数在外部调用方式来看和类的成员函数是一致,但是两者却有着本质的区别。

扩展函数和成员函数使用方式类似,可以直接访问被扩展类的方法和属性。(原理: 传入了一个扩展类的对象,内部实际上是用实例对象去访问扩展类的方法和属性);

扩展函数不能打破扩展类的封装性,不能像成员函数一样直接访问内部私有函数和属性。(原理: 原理很简单,扩展函数访问实际是类的对象访问,由于类的对象实例不能访问内部私有函数和属性,自然扩展函数也就不能访问内部私有函数和属性了);

扩展函数实际上是一个静态函数是处于类的外部,而成员函数则是类的内部函数;

父类成员函数可以被子类重写,而扩展函数则不行。

data class Item(val name: String, val price: Float)                                         // 1  

data class Order(val items: Collection<Item>)  

fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F    // 2  
fun Order.maxPricedItemName() = this.items.maxByOrNull { it.price }?.name ?: "NO_PRODUCTS"

val Order.commaDelimitedItemNames: String                                                   // 3
    get() = items.map { it.name }.joinToString()

fun main() {

    val order = Order(listOf(Item("Bread", 25.0F), Item("Wine", 29.0F), Item("Water", 12.0F)))
    
    println("Max priced item name: ${order.maxPricedItemName()}")                           // 4
    println("Max priced item value: ${order.maxPricedItemValue()}")
    println("Items: ${order.commaDelimitedItemNames}")                                      // 5

}

还可以扩展属性,其实就是function吧

When Statement

fun main() {
    cases("Hello")
    cases(1)
    cases(0L)
    cases(MyClass())
    cases("hello")
}

fun cases(obj: Any) {                                
    when (obj) {                                     // 1   
        1 -> println("One")                          // 2
        "Hello" -> println("Greeting")               // 3
        is Long -> println("Long")                   // 4
        !is String -> println("Not a string")        // 5
        else -> println("Unknown")                   // 6
    }   
}

class MyClass

When Expression

fun main() {
    println(whenAssign("Hello"))
    println(whenAssign(3.4))
    println(whenAssign(1))
    println(whenAssign(MyClass()))
}

fun whenAssign(obj: Any): Any {
    val result = when (obj) {                   // 1
        1 -> "one"                              // 2
        "Hello" -> 1                            // 3
        is Long -> false                        // 4
        else -> 42                              // 5
    }
    return result
}

class MyClass

可以看到kotlin的when语句可以代替各种if else,而不仅仅是枚举

object Expression

fun rentPrice(standardDays: Int, festivityDays: Int, specialDays: Int): Unit {  //1

    val dayRates = object {                                                     //2
        var standard: Int = 30 * standardDays
        var festivity: Int = 50 * festivityDays
        var special: Int = 100 * specialDays
    }

    val total = dayRates.standard + dayRates.festivity + dayRates.special       //3

    print("Total price: $$total")                                               //4

}

partition

val numbers = listOf(1, -2, 3, -4, 5, -6)                // 1

val evenOdd = numbers.partition { it % 2 == 0 }           // 2
val (positives, negatives) = numbers.partition { it > 0 } // 3

是个有趣的特性,但是只能分出2部分

Map:
如果某个值确信应该在Map里面,
那么使用


try {
    map.getValue("anotherKey")                              // 4
} catch (e: NoSuchElementException) {
    println("Message: $e")
}

比使用map["anotherKey"]!!要好,因为exception更明确

zip

val A = listOf("a", "b", "c")                  // 1
val B = listOf(1, 2, 3, 4)                     // 1

val resultPairs = A zip B                      // 2
val resultReduce = A.zip(B) { a, b -> "$a$b" } // 3

这个确实没看出来有多大用。

Delegation Pattern

interface SoundBehavior {                                                          // 1
    fun makeSound()
}

class ScreamBehavior(val n:String): SoundBehavior {                                // 2
    override fun makeSound() = println("${n.toUpperCase()} !!!")
}

class RockAndRollBehavior(val n:String): SoundBehavior {                           // 2
    override fun makeSound() = println("I'm The King of Rock 'N' Roll: $n")
}

// Tom Araya is the "singer" of Slayer
class TomAraya(n:String): SoundBehavior by ScreamBehavior(n)                       // 3

// You should know ;)
class ElvisPresley(n:String): SoundBehavior by RockAndRollBehavior(n)              // 3

fun main() {
    val tomAraya = TomAraya("Thrash Metal")
    tomAraya.makeSound()                                                           // 4
    val elvisPresley = ElvisPresley("Dancin' to the Jailhouse Rock.")
    elvisPresley.makeSound()
}

这个对设计模式来说还是很方便的,其实可以这么宽
class: interface by impl
这个class实现了这个interface,但是是代理给了impl

Coding conventions

需要经常去看

Basic types

Bitwise operations
Kotlin provides a set of bitwise operations on integer numbers. They operate on the binary level directly with bits of the numbers’ representation. Bitwise operations are represented by functions that can be called in infix form. They can be applied only to Int and Long:

val x = (1 shl 2) and 0x000FF000
Here is the complete list of bitwise operations:

shl(bits) – signed shift left

shr(bits) – signed shift right

ushr(bits) – unsigned shift right

and(bits) – bitwise AND

or(bits) – bitwise OR

xor(bits) – bitwise XOR

inv() – bitwise inversion

kotlin的位运算居然不能直接& ^ | , 必须调用infix function

Floating-point numbers comparison
The operations on floating-point numbers discussed in this section are:

Equality checks: a == b and a != b

Comparison operators: a < b, a > b, a <= b, a >= b

Range instantiation and range checks: a…b, x in a…b, x !in a…b

When the operands a and b are statically known to be Float or Double or their nullable counterparts (the type is declared or inferred or is a result of a smart cast), the operations on the numbers and the range that they form follow the IEEE 754 Standard for Floating-Point Arithmetic.

However, to support generic use cases and provide total ordering, the behavior is different for operands that are not statically typed as floating-point numbers. For example, Any, Comparable<…>, or Collection types. In this case, the operations use the equals and compareTo implementations for Float and Double. As a result:

NaN is considered equal to itself

NaN is considered greater than any other element including POSITIVE_INFINITY

-0.0 is considered less than 0.0

Here is an example that shows the difference in behavior between operands statically typed as floating-point numbers (Double.NaN) and operands not statically typed as floating-point numbers (listOf(T)).

println(Double.NaN == Double.NaN)                 // false
println(listOf(Double.NaN) == listOf(Double.NaN)) // true

println(0.0 == -0.0)                              // true
println(listOf(0.0) == listOf(-0.0))              // false

println(listOf(Double.NaN, Double.POSITIVE_INFINITY, 0.0, -0.0).sorted())
// [-0.0, 0.0, Infinity, NaN]

浮点数的比较真的很怪异

Raw strings
Raw strings can contain newlines and arbitrary text. It is delimited by a triple quote (“”"), contains no escaping and can contain newlines and any other characters:

val text = """
    for (c in "foo")
        print(c)
"""

To remove leading whitespace from raw strings, use the trimMargin() function:

val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

trimMargin比trimIndent更好

By default, a pipe symbol | is used as margin prefix, but you can choose another character and pass it as a parameter, like trimMargin(“>”).

val price = """
${'$'}_9.99
"""

${'$'},并不是\$

Primitive type arrays
Kotlin also has classes that represent arrays of primitive types without boxing overhead: ByteArray, ShortArray, IntArray, and so on. These classes have no inheritance relation to the Array class, but they have the same set of methods and properties. Each of them also has a corresponding factory function:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

之所以新创建一个IntArray而不是Array, 是因为IntArray是专为primitive type打造的,性能更好.

Type checks and casts
is and !is operators

Smart casts
val local variables - always, with the exception of local delegated properties.

val properties - if the property is private or internal or if the check is performed in the same module where the property is declared. Smart casts cannot be used on open properties or properties that have custom getters.

var local variables - if the variable is not modified between the check and the usage, is not captured in a lambda that modifies it, and is not a local delegated property.

var properties - never, because the variable can be modified at any time by other code.

“Unsafe” cast operator

val x: String = y as String

“Safe” (nullable) cast operator

val x: String? = y as? String

Control Flow

val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop
    }
}

fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
}

当迭代集合的时候,最好是用for循环而不是foreach,因为可以使用break和continue

The throw expression has the type Nothing. This type has no values and is used to mark code locations that can never be reached. In your own code, you can use Nothing to mark a function that never returns:

使用nothing来标记这个函数一定会发生异常,没有返回值。这样编译器就允许通过了。

Packages and imports

import org.example.Message // Message is accessible
import org.test.Message as TestMessage // TestMessage stands for 'org.test.Message'

允许重命名这点比java好很多

Visibility of top-level declarations
If a top-level declaration is marked private, it is private to the file it’s declared in (see Visibility modifiers).

Classes and objects

During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers:

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}")
    }
}

If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword:

class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

The override modifier is required for Circle.draw(). If it’s missing, the compiler will complain. If there is no open modifier on a function, like Shape.fill(), declaring a method with the same signature in a subclass is not allowed, either with override or without it. The open modifier has no effect when added to members of a final class – a class without an open modifier.
不允许override的memeber function就是不能重名,不管加不加override。这就很好,避免了c++的各种怪异的行为。

This means that when the base class constructor is executed, the properties declared or overridden in the derived class have not yet been initialized. Using any of those properties in the base class initialization logic (either directly or indirectly through another overridden open member implementation) may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, or init blocks.

这个行为跟java一样,parent在初始化的过程中不可以调用child的函数,因为child还没被初始化

Code in a derived class can call its superclass functions and property accessor implementations using the super keyword:

Inside an inner class, accessing the superclass of the outer class is done using the super keyword qualified with the outer class name: super@Outer:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

lateinit很好用,它让你能享受到kotlin的null check又允许你延迟初始化

Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property being accessed and the fact that it hasn’t been initialized.

Checking whether a lateinit var is initialized
To check whether a lateinit var has already been initialized, use .isInitialized on the reference to that property:

There are four visibility modifiers in Kotlin: private, protected, internal, and public. The default visibility is public.

Functions, properties, classes, objects, and interfaces can be declared at the “top-level” directly inside a package:
If you don’t use a visibility modifier, public is used by default, which means that your declarations will be visible everywhere.

If you mark a declaration as private, it will only be visible inside the file that contains the declaration.

If you mark it as internal, it will be visible everywhere in the same module.

The protected modifier is not available for top-level declarations.

Extensions are resolved statically
Extensions do not actually modify the classes they extend. By defining an extension, you are not inserting new members into a class, only making new functions callable with the dot-notation on variables of this type.

其实你创建一个extention function就是创建了一个static helper function。所以extension function不可以访问private的成员,也不能作为virtual function

Scope of extensions
In most cases, you define extensions on the top level, directly under packages:

To use an extension outside its declaring package, import it at the call site:

Data classes
The standard library provides the Pair and Triple classes. In most cases, though, named data classes are a better design choice because they make the code easier to read by providing meaningful names for the properties.

Pair,Triple, Data class其实就可以用来返回多个value

Sealed classes and interfaces
我们通常说不建议使用继承来实现代码复用,但是如果只是在一个很小的范围内是完全可以接受的。sealed class正式为此目的
A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.

Direct subclasses of sealed classes and interfaces must be declared in the same package.

Generics: in, out, where
“For maximum flexibility, use wildcard types on input parameters that represent producers or consumers”, and proposes the following mnemonic:

PECS stands for Producer-Extends, Consumer-Super.
out --> ? extends T
in --> ? supers T

The general rule is this: when a type parameter T of a class C is declared out, it may occur only in the out-position in the members of C
out 说明这个class是T的producer

Nested and inner classes

window.addMouseListener(object : MouseAdapter() {

    override fun mouseClicked(e: MouseEvent) { ... }

    override fun mouseEntered(e: MouseEvent) { ... }
})

Delegation

interface Base {
    fun print()
}

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

class Derived(b: Base) : Base by b

base must be an interface

Local functions

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

local functions are just like lamdbas
Use instances of a custom class that implements a function type as an interface:

class IntTransformer: (Int) -> Int {
    override operator fun invoke(x: Int): Int = TODO()
}

val intFunction: (Int) -> Int = IntTransformer()

this is weird

This expressions

Destruction:

{ a -> ... } // one parameter
{ a, b -> ... } // two parameters
{ (a, b) -> ... } // a destructured pair
{ (a, b), c -> ... } // a destructured pair and another parameter

加了括号就表示是一个参数,只是被deconstruct了

Get started with Kotlin/JVM

你可能感兴趣的:(kotlin,学习,笔记)