Kotlin学习笔记(Kotlin编程实战)

233 阅读7分钟

Kotlin学习笔记(Kotlin编程实战)

编译命令行

  • kotlinc-jvm -d d.jar XX.kt
  • java -jar d.jar
  • kotlinc-jvm XX.kt
  • javap -p XX.class
  • javap -c XX.class
  • kotlinc-jvm -classpath d.jar -script aa.kts

参考

协程

  • 异步
    • 并发
      • 同时进行,但可能时间片不同
    • 并行
      • 同时进行,时间片也相同,在不同的cpu核上.

image.png

在脚本中使用协程

#!/usr/bin/env kotlinc-jvm -classpath "/opt/kotlin-learn/kotlinx-coroutines-core-1.2.2.jar" -script

import kotlinx.coroutines.*

jar包的地址需要使用绝对地址,这样脚本才能找到相应的jar包


import kotlin.system.measureTimeMillis
import kotlinx.coroutines.*


println("sync")
// suspend ,launch ,withContext
suspend fun task1() {
    println("start task1 in Thread ${Thread.currentThread()}")
    yield()
    println("end task1 in Thread ${Thread.currentThread()}")
}


suspend fun task2() {
    println("start task2 in Thread ${Thread.currentThread()}")
    yield()
    println("end task2 in Thread ${Thread.currentThread()}")
}



GlobalScope.launch {
    withContext(Dispatchers.IO) {
        delay(100)
        task1()
    }
    println("call task1 in Thread ${Thread.currentThread()}")
}

GlobalScope.launch(Dispatchers.Default) {
    task2()
    println("call task2 in Thread ${Thread.currentThread()}")
}

//async And await


GlobalScope.launch {
    val count = async(Dispatchers.IO) {
        println("fetching in ${Thread.currentThread()}")
        Runtime.getRuntime().availableProcessors()
    }

    println("Called the function in ${Thread.currentThread()}")
    println("Number of cores is ${count.await()}")
}

递归和记忆

  • 递归
  • 尾递归
  • 记忆,Compose中的remember应该用的就是这个

//递归


fun fast_sort(numbers:List<Int>) :List<Int> {
    if(numbers.isEmpty()) {
        return numbers
    }else {
        val first = numbers.first()
        val tail = numbers.drop(1)

        val lessAndEq = tail.filter { it -> it <= first }
        val more =tail.filter{it -> it > first }
        // println(lessAndEq)
        // println(more)
        
        return fast_sort(lessAndEq)+first+fast_sort(more)
    }
}

println(fast_sort(listOf(3,8,20,9,6,30)))

println(listOf(1,2,3,4)+listOf(4,5,6))

//尾递归
tailrec fun factorialRec(n:Int) :BigInteger = if(n <= 0) 1.toBigInteger() else n.toBigInteger()*factorialRec(n-1)

println(factorialRec(5000))
println(factorialIterative(50000))

fun factorialIterative(n:Int) = (1..n).fold(BigInteger("1")) { result,e -> result*e.toBigInteger()}

println(factorialIterative(5000))
println(factorialIterative(50000))


//记忆

fun fib(n:Int):Long  {
    println(n)
   return when(n) {
    0,1 -> 1
    else -> fib(n-1)+fib(n-2)
}
}

println(fib(5))
// println(fib(40))
// println(fib(45))
// println(measureTimeMillis { fib(40)})
// println(measureTimeMillis { fib(45)})
// println(measureTimeMillis { fib(100)})

fun <T,R> ((T)->R).memoize():((T)->R) {
    val original = this
    val cache = mutableMapOf<T,R>()

    val result = {n:T -> cache.getOrPut(n) {original(n)}}
    // println(result)
    return result
}

lateinit var fib2:(Int)->Long

fib2 = { n:Int -> 
      when(n) {
          0,1 -> 1L
          else -> fib2(n-1)+fib2(n-2)
      }
}.memoize()

println(fib2(40))
println( fib2(45))
println(  fib2(145))

println(measureTimeMillis { fib2(40)})
println(measureTimeMillis { fib2(45)})
println(measureTimeMillis { fib2(145)})

class Memoize<T,R>(val func:(T) -> R) {
    val cache = mutableMapOf<T,R>()

    operator fun getValue(thisRef:Any?,property:KProperty<*>) = { n:T ->
       cache.getOrPut(n) { func(n) }
    }
}


val fib3:(Int) -> Long by Memoize { n:Int -> 
      when(n) {
          0,1->1L
          else -> fib3(n-1)+fib3(n-2)
      }
}

//钢条切割问题

val prices = mapOf(1 to 2,2 to 4,3 to 6,4 to 7,5 to 10,6 to 17, 7 to 17)

val maxPrice:(Int)->Int by Memoize { length:Int ->
     val priceAtLength = prices.getOrDefault(length,0)

     (1 until length).fold(priceAtLength) { max,cutLength -> 
          val cutPrice = maxPrice(cutLength)+maxPrice( length - cutLength)
          Math.max(cutPrice,max)
     }
}

for(i in 1..7) {
    println("For length $i max price is ${maxPrice(i)}")
}


Kotlin DSL


infix fun Int.days(timing:Tense) = DateUtil(this,timing)

enum class Tense { 
       ago,from_now
}


class DateUtil(val number:Int,val tense:Tense) {

    override fun toString():String {
        val today = Calendar.getInstance()

        when(tense) {
            Tense.ago -> today.add(Calendar.DAY_OF_MONTH,-number)
            Tense.from_now -> today.add(Calendar.DAY_OF_MONTH,number)
            else -> {}
        }
       return today.getTime().toString()
    }
}

println(2 days Tense.ago)
println(3 days Tense.from_now)



//带有方法的DSL

class Meeting(val title:String) {
    var startTime = ""
    var endTime = ""

    val start = this
    val end = this

    private fun convertToString(time:Double) = String.format("%.02f",time)

    infix fun at(time:Double) {
        startTime = convertToString(time)
    }

    infix fun by(time:Double) {
        endTime = convertToString(time)
    }

    override fun toString() = "$title Meeting starts $startTime ends $endTime"
}

infix fun String.meeting(block:Meeting.() -> Unit) {
    val meeting = Meeting(this)
    meeting.block()

    println(meeting)
}


"Release Planning" meeting {
   start at 14.30
   end by 15.20

   println("With in lambda: $this")
}





//xml解析器

@DslMarker
annotation class XMLMarker

fun xml(block:XMLBuilder.()->Node):Node = XMLBuilder().run(block)

@XMLMarker
class XMLBuilder {
    fun root(rootElementName:String,block:Node.()->Unit):Node = Node(rootElementName).apply(block)
}

@XMLMarker
class Node(val name:String) {
    var attributes:Map<String,String> = mutableMapOf()
    var children:List<Node> = listOf()
    var textValue:String =  ""

    fun text(value:String) { textValue = value }

    fun element(childName:String, vararg attributeValues:Pair<String,String>, block:Node.() -> Unit):Node {
        val child = Node(childName) 
        attributeValues.forEach {
            child.attributes+=it
        }
        children +=child
        return child.apply(block)
    }


    fun toString(indentation:Int) :String {
        val attributesValues = if(attributes.isEmpty()) "" else attributes.map {
            "${it.key}='${it.value}'"}.joinToString(" "," ") 

        val DEPTH = 2
        val indent = " ".repeat(indentation) 

        return if(!textValue.isEmpty()) "$indent<$name$attributesValues>$textValue</$name>"
        else 
          """$indent<$name$attributesValues>
        |${children.joinToString("\n") { it.toString(indentation + DEPTH)}}
        |$indent</$name>""".trimMargin()
    }

    override fun toString() = this.toString(0)
}

val langsAndAuthors = mapOf("JavaScript" to "Eich","Java" to "Gosling","Ruby" to "Matz")

val xmlString = xml {
    root("languages") {
        langsAndAuthors.forEach { name,author ->
              element("language","name" to name) {
                  element("author") {
                      text(author)
                    //   this@xml.root("hahahaha"){}
                  }
              }
        }
    }
}

println(xmlString)

Kotlin其他特性

  • 操作符重写
  • 扩展方法
  • 扩展属性
  • 中缀操作符
    • 只能一个参数
    • 不能使用vararg
  • 常用的lambda
    • let
    • run
    • also
    • apply
    • 使用特点
      • □所有四个方法都执行传给它们的lambda。
      • □let()和run()执行lambda,并将lambda的结果返回给调用方。
      • □also()和apply()忽略lambda的结果,而是将context对象返回给它们的调用方。
      • □run()和apply()在调用它们的context对象的上下文this的执行过程中运行lambda。

操作符重写

operator fun String.times(n:Int):String {
    var r = this
    repeat(n) {
        r+=this
    }
    return r
}

println("message "*10)

支持的操作符


+x x.unaryPlus()
-x x.unaryMinus()
!x x.not()
x+y x.plus(y)
x-y x.minus(y)
x*y x.times(y)
x/y x.div(y)

x%y x.rem(y)
++y x.inc()
--x x.dec()
x==y x.equals(y)
x!=y !(x.equals(y))
x<y x.compareTo(y)
x[i] x.get(i)
x[i]=y x.set(i,y)
y in x x.contains(y)
x..y x.rangeTo(y)
x()  x.invoke()
x(y) x.invoke(y)

扩展方法


data class Point(val x:Int,val y:Int)
data class Circle(val cx:Int,val cy:Int,val radius:Int)
//扩展方法+操作符重写
operator fun Circle.contains(point:Point):Boolean {
    return (point.x - cx) * (point.x - cx) + (point.y -cy)*(point.y - cy) < radius*radius
}

val circle = Circle(100,100,24)
val point1 = Point(110,100)

println(circle.contains(point1))
println(point1 in circle)

//扩展属性
val Circle.area :Double 
get() = kotlin.math.PI * radius * radius


println(circle.area)


for(word in 1..10) {
    println(word)
}

operator fun ClosedRange<String>.iterator() = object:Iterator<String> {
    private var next = start 
    private val last = endInclusive
    override fun hasNext():Boolean {
        println("last[0]:${last[0]},next[0]:${next[0]}")
        return last[0]>next[0]
    }

    override fun next():String {
        println("next:$next,last:$last")
        val b = (next[0]+1).toString()
        next = b
        return b 
    }
}

for(word in "a".."z") {
    println(word)
}


//类内注入扩展函数

data class Point(val x:Int,val y:Int) {
    val pair = Pair(x,y)
    private val firstSign = if(pair.first < 0) "" else "+"
    private val secondSign = if(pair.second < 0) "" else "+"

    override fun toString() = pair.point2String()



    //两个this的使用
    fun Pair<Int,Int>.point2String() = "(${firstSign}${first}, ${this@Point.secondSign}${this.second})"
}

val p = Point(10,-10)
println(p)


//我们的高级函数g(f(x))
fun <T,U,R> ((T)->U).andThen(next:(U)->R):(T)->R = { input:T -> next(this(input))}

fun increment(number:Int):Double = number+1.toDouble()
fun double(number:Double) = number*2

val incrementAndDouble = ::increment.andThen(::double)

println(incrementAndDouble(5))



// 中缀操作符

infix fun Circle.sameCenterPoint(circle:Circle):Boolean {
    return this.cx == circle.cx && this.cy == circle.cy
}

val c1 = Circle(100,100,300)
val c2 = Circle(100,100,40)

println(c1 sameCenterPoint c2)

函数编程

  • 高阶参数
    • 函数引用
    • 函数参数
    • 函数返回函数
    • lambda,闭包和词法作用域
    • inline,noinline,crossline
    • 迭代器
      • 内部迭代器
      • 外部迭代器
    • 序列

序列


val nameOfFirstAdult2 = people.asSequence().filter(::isAdult).map(::fetchFirstName).take(10)
println(nameOfFirstAdult2.toList())


fun nextOod(n:Int):Int = if(n % 2 == 0) n+2 else n+1

val oods = generateSequence(100,::nextOod)

println(oods.take(8).toList())


// fun nextRandom(seed:Int):Int = Random(seed).nextInt()

// val randoms = generateSequence(10,::nextRandom)

// println(randoms.take(10).toList())

val oods2 = sequence {
    var i:Int = 0
    while(true) {
        i++
        if(i % 2 == 0) {
            yield(i)
        }
    }
}

println(oods2.drop(2).take(10).toList())



迭代器

  • 内部迭代器
    • filter
    • map
    • reduce
    • flatMap
    • fltten
    • sorted
    • groupby


val numbers = listOf(1,4,5,9,10,20,40)

numbers.filter{ e -> e % 2 == 0 }.forEach { 
    println(it)
}

val doubleEven = numbers.filter{ e -> e % 2 == 0 }.map{ e -> e * 2 }
println(doubleEven)


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

val people = listOf(
    Person("Kotlin",5),
    Person("Java",10),
    Person("C",20),
    Person("C#",10),
    Person("Python",20),
    Person("JavaScript",15),
    Person("C++",20)
)
println(people)

val result = people.filter{ person -> (person.age > 10) }
.map { person -> person.name }
.map { name ->  name.toUpperCase() }
.reduce { names, name -> "$names # $name"}


val totalAge = people.map { person -> person.age }.reduce { total,age -> total + age}

val m = people.filter{person -> person.age > 10}.first().name
val takeSample = people.filter{person -> person.age > 10}.take(10)
println(m)
println(result)
println(totalAge)
println(takeSample)


val animals = listOf(
    listOf(Person("Dog1",2),Person("Cat1",3)),
    listOf(Person("Dog2",2),Person("Cat2",4))
)

println(animals.flatten())

val namesAndReversed = people.map { person -> person.name }
.map(String::toLowerCase)
.map { name -> listOf(name,name.reversed())}

println(namesAndReversed)

println(namesAndReversed.flatten())


val namesAndReversed2 = people.map { person -> person.name }
.map(String::toLowerCase)
.flatMap { name -> listOf(name,name.reversed())}

println(namesAndReversed2)

val namesSortedByAge = people.sortedBy{person -> person.age }
.map{person -> person.name }

println(namesSortedByAge)

val groupBy1stLetter = people.groupBy { person -> person.name.first() }

println(groupBy1stLetter)


lambda,闭包和词法作用域

  • 什么是lambda
    • lambda是无状态的,输出取决于输入参数的值
  • 什么是闭包
    • 依赖外部状态。这样的lambda被称为闭包——这是因为它关闭了定义范围,来绑定到非局部的属性和方法
  • 什么是词法作用域
    • 外部变量闭包的定义范围——即,闭包的主体是在哪里定义的,这称为词法作用域
  • factor如果是可变量,闭包会有不同预期,尽量避免
//lambda
val doubleIt = { e:Int -> e * 2}

//闭包
val factor = 2
val doubleIt2 = {e:Int -> e* factor}


//factor2是变量的情况下的输出

var factor2 = 2
factor2 = 3
val doubled = listOf(1,2).map{it * factor2}
doubled.forEach { print(it)}
println("line")
factor2 = 0
doubled.forEach { print(it)}
println("line")

函数返回函数


val names = listOf("Pam","Ppam","Paul","Paula")

println(names.find {name -> name.length == 5})

fun predicateOfLength(length:Int):(String)->Boolean {
    return {input:String -> input.length == length}
}

println(names.find(predicateOfLength(4)))


函数引用



fun walkTo(n:Int,action:(Int)->Unit) {
    (1..n).forEach{
        action(it)
    }
}

walkTo(5) {
    println(it)
}

walkTo(10,System.out::println)

object Terminal {
    fun write(value:Int)  = println(value)
}

walkTo(5,Terminal::write)



面向对象

  • 泛型类
  • 匿名对象
  • 单例对象
  • 接口和抽象类
  • 内部类
  • 派生类
  • 委托方式
    • 类委托
    • 属性委托
    • 延迟加载
    • 短路求值
    • Observable

委托方式

  • 委托方式是低耦合,继承方式是高耦合
  • 委托方式相比较传入对象的方式,减少了很多模板代码
  • 委托方式可以重写方法
  • 继承是可替代的.委托是不可替代的.

interface Worker {
    fun work()
    fun takeVacation()

}

class JavaWorker:Worker {
    override fun work() = println("write Java...")
    override fun takeVacation() = println("code at the beach....")
}

class KotlinWorker:Worker {
    override fun work() = println("write Kotlin ....")
    override fun takeVacation() = println("branch at the ranch...")
}


class ManagerBefore(val worker:Worker) {
    fun work() = worker.work()
    fun takeVacation() = worker.takeVacation()
}

class ManagerBy:Worker by JavaWorker()

class ManagerByV2(val staff:Worker):Worker by staff {
    fun meeting() = println("the worker's class ${staff.javaClass.simpleName}")
    override fun takeVacation() = println("handle by Manager")
}

val doe = ManagerByV2(KotlinWorker())
val roe = ManagerByV2(JavaWorker())

doe.work()
doe.meeting()
doe.takeVacation()

roe.work()
roe.meeting()



属性委托


var comment:String = "Some nice message"
println(comment)
comment = "This is Stupid"
println(comment)


import kotlin.reflect.KProperty
class PoliteString(var content:String) {
    operator fun getValue(thisRef:Any?,property:KProperty<*>) = content.replace("stupid","s****")
    operator fun setValue(thisRef:Any,property:KProperty<*>,value:String)  {
        content = value
    }
}


var comment2:String by PoliteString(comment)
println(comment2)

Observable监控值修改

var count by observable(0) { property ,oldValue,newValue -> 
    println("Property: $property,old:$oldValue,new:$newValue")
}

println("count:$count")
count++
println("count:$count")
count--
println("count:$count")

var message by observable("") { property ,oldValue,newValue -> 
    println("Property: $property,old:$oldValue,new:$newValue")
}

println("message:$message")
message = "message1"
println("message:$message")

vetoable支持拒绝修改

var message2 by vetoable("") { property ,oldValue,newValue -> 
    println("Property: $property,old:$oldValue,new:$newValue")
    newValue != "message2"
}

println("message2:$message2")
message2 = "message1"
println("message2:$message2")
message2 = "message2"
println("message2:$message2")

派生类和枚举

在Kotlin中,存在一种极端情况,我们有final类——即未标记为open的类,它们不能有任何派生类。在另一种极端情况,我们有open类和abstract类,不知道哪个类可以继承它们。最好能有一个中间地带,让一个类只作为几个类的基,这几个类是由类的创建者指定的。

  • 枚举和派生类都知道有几种类型
  • 派生类可以有很多实例
  • 枚举每种类型只有一个实例
    • 枚举可以定义定义方法
    • 枚举可以重写方法

enum class Suit(val symbol:Char) { 
    CLUBS('\u2663'),DIAMONDS('\u2666'){
        override fun display() = "${super.display()}-${symbol}"
    } ,HEARTS('\u2665'),SPADES('\u2660');

    open fun display() = "$symbol $name"
}

sealed class Card(val suit:Suit)

class Ace(suit:Suit):Card(suit)
class King(suit:Suit):Card(suit)
class Queen(suit:Suit):Card(suit)
class Jack(suit:Suit):Card(suit)
class OtherCard(suit:Suit):Card(suit)
object OtherObject : Card(Suit.DIAMONDS) {

}

for(suit in Suit.values()) {
    println(suit.display())
}

println(Ace(Suit.DIAMONDS).suit.display())
println(OtherObject.suit)



内部类



class TVV2 {
    private var volumn = 0

    inner class TVRemoteV2:Remote {
        override fun up() {
            volumn++
        }
        override fun down() {
            volumn--
        }
    }

    val remote:Remote
       get() = TVRemoteV2()

    override fun toString():String = "Volumn :${volumn}"
}


val tv_v2 = TVV2()
val remote_v2 = tv_v2.remote

println("$tv_v2")
remote_v2.up()
println("$tv_v2")
remote_v2.down()
println("$tv_v2")

接口和抽象类

  • 接口可以实现多个,抽象类只能继承一个
  • 接口不能有属性,抽象类可以有属性
  • 类更多的是继承状态,处理状态
  • 接口更多的行为和契约.


interface Remote {
    fun up()
    fun down()
    fun doubleUp() {
        up()
        up()
    }
}

class TV {
    var volumn = 0

    override fun toString():String {
        return "The TV's volumn is ${volumn}"
    }
}

class TVRemote(val tv:TV):Remote {
    override fun up() {
        tv.volumn ++ 
        println(tv)
    }

    override fun down() {
        tv.volumn --
        myPrintln(tv)
    }
}

fun myPrintln(msg:Any) {
    println(msg)
}
val tv = TV()
val remote = TVRemote(tv)

remote.up()
remote.down()
remote.doubleUp()

abstract class Muiscian(val name:String ,val activeFrom:Int) {
    abstract fun instrumentType():String
}

open class Cellist(name:String,activeFrom:Int):Muiscian(name,activeFrom) {
    override fun instrumentType():String = "instrumentType"
}

class CellistChild(name:String,activeFrom:Int):Cellist(name,activeFrom) {
    override fun instrumentType():String = "instrumentType from Child"
}


val ma = Cellist("YoyoMa",1986)
val maChild = CellistChild("HahaMa",1982)

println(ma.instrumentType())
println(maChild.instrumentType())

泛型类

class PriorityPair<T:Comparable<T>>(member1:T,member2:T) {
    val first:T
    val second :T

    init {
        if(member1 >= member2) {
            first = member1
            second = member2
        }else {
            first = member2
            second = member1
        }
    }

    override fun toString() = "${first} ${second}"
}

println(PriorityPair(2,1))
println(PriorityPair("A","B"))

匿名对象


fun drawCircle() {
    val circle = object {
        val x = 100
        val y = 100
        val radius = 100

        override fun toString():String {
            return "($x,$y) $radius"
        }
    }

    println(circle)
}

drawCircle()


单例对象

object Util {
    fun numberOfProcessors() = Runtime.getRuntime().availableProcessors()
}


println(Util.numberOfProcessors())

使用工厂创建对象


class MachineOperator private constructor(val name:String) {

    private fun checkin() {
        println("checkin $name")
    }

    companion object {

        fun create(name:String) :MachineOperator {
            val instance = MachineOperator(name)
            instance.checkin()
            return instance
        }
    }
}

val operator = MachineOperator.create("Master")
println(operator.name)


泛型学习

  • 普通的函数参数传入
  • 带有集合泛型的参数传入
  • 什么是使用点协变
  • 什么是声明点协变
  • 什么是逆变
    • 协变是读
    • out
    • 允许接受子类的集合
  • 什么是逆变
    • 逆变是写
    • in
    • 允许接受父类的集合
  • Where来实现多个接口的对象限定
  • 类型擦除

逆变和协变

//函数
fun copyFromTo(from:Array<out Fruit>,to:Array<in Fruit>) {
    for(i in 0 until from.size) {
        to[i] = from[i]
    }
}


val fruits1 = Array<Fruit>(3) { _ -> Fruit()}
val bananas3 = Array<Banana>(3) { _ -> Banana()}
//协变
copyFromTo(bananas3,fruits1)
// copyFromToNoOut(bananas3,fruits1)

//逆变
val things = Array<Any>(3) { _ -> Fruit()}

copyFromTo(bananas3,things)



Where限定符

interface AutoClosable {
    fun close()
}

fun <T:AutoClosable> useAndClose(input:T) {
    input.close()
}


fun <T> useAndCloseAndAppend(input:T ) where T:AutoClosable,T:Appendable {
    input.append("there")
    input.close()
}

*限定符

  • 只能读
  • 不能写
  • 可以传入所有类型的Array
fun printAllValues(values:Array<*>) {
    for(value in values) {
        println(value)
    }
}

printAllValues(arrayOf(2,3,4))

类型擦除

java中因为类型擦除,通过传入类型来查找对象


abstract class Book(val name:String) 
class Fiction(name:String):Book(name)
class NonFiction(name:String):Book(name)


val books  = listOf(Fiction("book1"),Fiction("book2"),NonFiction("nobook1"))

fun <T> findFirst(books:List<Book>,ofClass:Class<T>):T {
    val selected = books.filter { book -> ofClass.isInstance(book)}

    if(selected.size == 0) {
        throw RuntimeException("Not Found")
    }

    return ofClass.cast(selected[0])
}

println(findFirst(books,NonFiction::class.java).name)



kotlin通过reified 来保留类型.



inline fun <reified T> findFirstByKotlin(books:List<Book>):T {
    val selected = books.filter { book -> book is T }

    if(selected.size == 0) {
        throw RuntimeException("Not Found")
    }

    return  selected[0] as T
}

println(findFirstByKotlin<NonFiction>(books).name)