Kotlin Essential

244 阅读5分钟

Pair

val equipment = "fishent" to "catching fish"
println(equipment.first)
//output
fishnet
val equipment = "fishent" to "catching fish" to "of big size" to "and strong" 
println(equipment)
//output
(((fishnet, catching fish), of big size), and strong)

println(equipment.first)
//output
((fishnet, catching fish), of big size)

println(equipment.first.first)
//output
(fishnet, catching fisht)

val equip = ("fishnet" to "catching fish" to ("of big size" to "and strong"))
val fishnet = "fishent" to "catching fish"
val (tool, use) = fishnet
println("The $tool is a tool for $use")
//output
The fishnet is a tool for catching fish
val fishnetString = fishnet.toString()
println(fishnetString)
//output
(fishnet, catching fish)

println(fishnet.toList())
//output
[fishnet, catching fish]
fun giveMeATool(): Pair<String, String>{
	return ("fishnet" to "catching")
}

val (tool, use) = giveMeATool()
println(tool)
//output
fishnemt
println(use)
//output
catching

Collections

listOf(): cannot change

  1. listOf().toReverse() 不会修改原来的list,而是重新创建一个新的list

  2. listOf(1,3,5).sum() :等于9

  3. 如果是不知道要怎么做sum的参数可以用sumBy: takes a lambda that specifies what property of the elements to summarize

    listOf("a","b","cc").sumBy {it.length} 等于 4

mutableListOf(): can change

val symptoms = mutableListOf("white spots", "red spots", "not eating", "bloated", "belly up")

symptoms.add()
symptoms.remove()

symptoms.contains("red")  //false
symptoms.comtains("red spots")  //true

// subList(firstArguments, endExcludingIndex )
println(symptoms.subList(4, symptoms.size)) // [belly up]

mapping

val cures = mapOf("white spots" to "Ich", "red sores" to "hole disease")
println(cures.get("white spots))
println(cures["white spots"])
  • getOrDefault: 如果我们map的东西没有值,找不到,还可以给一个default value:即 cures里找不到bloated → 那么就会返回一个默认值"sorry I don't know"
println(cures.getOrDefault("bloated", "sorry I don't know"))
  • 如果不只想要一个返回值的话,还可以用 getOrElse 花括号内的语句是可以被执行的,可以用来比如返回一个网页等等
cures.getOrElse("bloated:) { "No cure for this" }

mutableMapOf(): use to put & remove items

val inventory = mutableMapOf("fishnet" to 1)
inventory.put("tank scrubber", 3)
inventory.remove("fishnet", 1)

listOf() & mapOf() make immutable collections: can add & remove → 多用于线程中,以免很多的线程都需要接触这个collection mutableListOf() & mutableMapOf() make mutable collections: can modify

Constants

区别在于 const val的值是在编译的时候才确定的,但是val是在程序执行的时候assign和确定的

val num = 3
const val num = 5
val number = getNumberFun()
const val cantGetNumber = getNumberFun() // 这样是不可以的,因为const val 是在编译的时候就要分配值,等不到这个函数运行完返回值

const val

  1. const 只允许在top-level级别和object中声明

    • top-level就是位于代码文件的最外部,比如常见的类(非内部类和嵌套类)就是在top-level。意思是在结构上常量不属于任何的类,而是属于文件
    • object中可以指的是最外部的object也可以指的是companion object.
  2. 必须修饰val

  3. const val 可见性为public final static,可以直接访问。

    val 可见性为private final static,并且val 会生成方法getNormalObject(),通过方法调用访问。

    当定义常量时,出于效率考虑,我们应该使用const val方式,避免频繁函数调用。

companion object: initialized from the static constructor of the containing class → 在对象创建的时候创建

plain object: are initialized lazily on the first access to that object → 即 第一次使用的时候

object Constants{
	const val CONSTANT1 = "object constant"
}
val foo = Constants.CONSTANT1

class MyClass {
//因为没有一个top level的class, 所有要在class里面用 const的话就要用companion object给包起来
//而 companion object的特点就是在对象初次创建的时候就给创建,即编译的时候就能确定值
//但普通的object是在第一次使用的时候才赋值,即运行的时候再赋值,所以在class里面,要用companion obj
	companion object {
		const val CONSTANT2 = "constant inside companion"
	}
}

Extension Function

作用:add function到已有的一个class,但是不需要访问其内部的代码

fun String.hasSpaces() : Boolean {
	val found = this.find{it == ' '}
	return found != null
}

fun extensionExample() {
	"Does it has spaces".hasSpaces()   // true
}

简化一下:

fun String.hasSpaces() = find { it==' ' } != null

fun extensionExample() {
	"Does it has spaces".hasSpaces()   // true
}

很适合用在给不属于自己的class添加功能 / 函数

class AquariumPlant(val color: String, private size:Int)

fun AquariumPlant.isRed() = color == "Red"  //通常可以用来作为 helper function

//会报错,因为extension class只能访问public variable, 不能访问私有的
//就像一开始说得,extension fun是不会去访问内部代码的
fun AquariumPlant.isBig() = size > 50  

extension function resolved statically → 在编译的时候就确定了, 下面的例子中,AquariumPlant的print()函数在定义的时候就已经决定了输出 "Aquarium Plant",然后在编译阶段就确定了这个值,所以后面是不会再变了。因而,即使 aquariumPlant实际是等于 plant : GreenAquariumPlant的,也不会输出"Green Aquarium plant"

open class AquariumPlant(val color:String, private val size : Int)
class GreenAquariumPlant(size:Int) : AquariumPlant("Green", size)

fun AquariumPlant.print() = println("Aquarium Plant")
fun GreenAquariumPlant.print() = println("Green Aquarium plant")

fun staticExample() {
	val plant = GreenAquariumPlant(size = 50)
	plant.print()  // Green Aquarium plant

	val aquariumPlant : AquariumPlant = plant
	plant.print() // Aquarium Plant
}

Extensions

Extension Properties

Similarly to functions, Kotlin supports extension properties:

val AquariumPlant.isGreen:Boolean
		get() = color == "Green"

fun propertyExample(){
	val plant = AquariumPlant("Green", 50)
	plant.isGreen  //true
}

Note that, since extensions do not actually insert members into classes, there's no efficient way for an extension property to have a backing field. This is why initializers are not allowed for extension properties.即 extension property 不能直接初始化。 Their behavior can only be defined by explicitly providing getters/setters.

Example:

val  AquariumPlant.isGreen:Boolean = color == "Green"  // error: initializers are not allowed for extension properties
val House.number = 1 // error: initializers are not allowed for extension properties

Nullable receiver

Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null, and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.

fun Any?.toString(): String {
    if (this == null) return "null"
    // after the null check, 'this' is autocast to a non-null type, so the toString() below
    // resolves to the member function of the Any class
    return toString()
}

fun AquariumPlant?.pull() {
	this?.apply{
		println("removing $this")   //即 this 可以为null
	}
}

fun nullableExample() {
	val plant : AquariumPlant? = null
	plant.pull()  //可以成功运行的,不是错的。 可以接受空
}

Companion Object Extensions

class MyClass {
    companion object { }  // will be called "Companion"
}

fun MyClass.Companion.printCompanion() { println("companion") }

fun main() {
    MyClass.printCompanion()
}

Generic Class

作用:

generic class可以让一个class变成一个通用的class, 不受参数类型所限制,例如下面的例子,其实是同样的class,就因为类型不同,就要重新写一个class,如果使用generic class 的话,就能够是一个通用型的class, 传入一个type(T),根据T来判断类型

class MyIntList {
	fun get(pos: Int): Int { return 0}
	fun addItem(item:Int) {}
}
class MyStringList {
	fun get(pos: Int): String { return 0}
	fun addItem(item: String) {}
}
class MyList<T> {  // T stand for type, 但是也可以换成其他的东西,只是通常用T
	fun get(pos: Int): T {return 0}
	fun addItem(item : T) {}
}

fun workWithMyList() {
	val intList: MyList<String>
	val fishList: MyList<Fish>
}

Example

open class WaterSupply(var needsProcessed:Boolean)

class TapWater : WaterSupply(true) {
	fun addChemicalCleaners() {
		needsProcessed = false
	}
}
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
	fun filter() {
		needsProcessed = false
	}
}
class Aquarium<T>(val waterSupply: T)

fun genericExample() {
	val aquarium = Aquarium<TapWater>(TapWater())  //TapWater类型
	aquarium.waterSupply.addChemicalCleaners()   //不需要做另外的cast,可以直接调用TapWater这个class所拥有的方法

	/*****下面这个方法也是可以的***/
	val aquarium = Aquarium(TapWater())  //不用写<TapWater>,因为可以从参数的类型给推断出来是tapwater
	aquarium.waterSupply.addChemicalCleaners() 
}

Type 类型

如果按上面的例子,那么这个class可以是任意类型的,包括null

class Aquarium<T>(val waterSupply: T)

fun genericExample() {

	val aquarium = Aquarium(String)
	println(aquarium.waterSupply)  // ok的

	val aquarium = Aquarium(null)
	aquarium.waterSupply   //这样也不会报错因为T如果不加限定就可以为任何类型

}

// 所以上面的class 和 下面的class是等价的
class Aquarium<T:Any?> (val waterSuppy: T)  // 即: <T>  ==  <T:any?>

那么自然,如果不想要接受null这个类型,就要去掉 ?

class Aquarium<T : Any> (val waterSuppy: T)
fun genericExample() {
	val aquarium = Aquarium(null)
//现在这个地方就会报错了, not satisfied: inferred type nothing? is no subtype of Any
	aquarium.waterSupply   
}

如果想再给 T一个具体的类型:

class Aquarium<T : WaterSupply> (val waterSupply: T)
fun genericExample() {
	val aquarium = Aquarium(String)
	//那么现在这个地方就会报错了, 因为string不是WaterSupply类型
	//但是WaterTap, FishStoreWater,LakeWater 都可以,因为它们都继承了WaterSupply, 都是它的subtype
	aquarium.waterSupply   
}

check()assertion()差不多:throw an illegal exception if its argument is false

class Aquarium<T : WaterSupply> (val waterSupply: T) {
	fun addWater() {
		check( !waterSupply.needsProcessed ){"water supply needs processed"}
		println("add water from $waterSupply")
	}
}

上面的check意思是,如果不需要processed, 那么就执行println()语句,不然抛出带有error message:"water supply needs processed"的异常,

fun genericExample() {
	val aquarium = Aquarium(LakeWater())
	aquarium.waterSupply.filter()   // lakewater是需要处理的,如果没有这一条语句,直接执行下面一条语句,那么根据class中的check()语句,就会抛出异常,说 water needs  processed.
	aquarium.addWater()   
}

Generic In and Out

Generic Function

Annotation

Labeled Breaks