Kotlin (一)基础

作者: zcwfeng | 来源:发表于2020-12-18 17:56 被阅读0次

    基础语法

    1.1 增强类型推导

    fun main() {
        // 类型推断
        val string= "I am kotlin"
        val int = 1314
        val long = 1314L
        val float = 13.14f
        val double = 13.14
        val double2 = 10.1e6
        println(string.javaClass.name)
        println(int.javaClass.name)
        println(long.javaClass.name)
        println(float.javaClass.name)
        println(double.javaClass.name)
        println(double2.javaClass.name)
    }
    

    类型推导是Kotlin在java语言上的增强。编译器可以在不显示声明情况推导出类型。Kotlin 是属于静态语言,所以编写中会写很多类型,类型推导改善这点,提高开发效率。

    1.2 声明函数返回类型

    fun sum(x:Int,y:Int): Int { return x + y }
    
    必须返回类型:Int 否则 报错,默认是Unit 
    
    fun sum(x:Int,y:Int) = x + y
    
    我们可以这么写
    
    

    但是别高兴太早

    // 递归函数,全局类型推导不支持
    fun foo(n:Int) = if (n==0)  1 else n * foo(n -1)
    
    Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
    
    所以例如递归复杂的情况,使用表达式函数也必须有返回值声明
    
    fun foo(n:Int):Int = if (n==0)  1 else n * foo(n -1)
    
    原因是Kotlin,Scala支持的是子类型和继承,很难做到全局类型推导
    
    

    1.3 val 和var 的使用规则

    var ===> variable 可变
    val ===> 相当于 variable + final 不可变

     // val var
        val x = intArrayOf(1,2)
    //    x = intArrayOf(2,3)-> error
        x[0] = 2
    

    不同语言对数组设计不同,在Swift 中 let 是不能像我们这样改变值的。Swift 数组看做值类型,提一下作对比,具体去看Swift语言

    kotlin 设计考虑数组这种大数据结构拷贝成本,所以数组存在堆内存。kotlin val 引用不能更改,但是引用的对象可以重新赋值

    class Book(var name:String){
        fun printName(){
            println(name)
        }
    }
    
        val book = Book("Thinking in Java")
        book.name = "I use kt"
        book.printName()
    

    优先使用val来避免副作用

    尽可能采用val,不可变对象以及纯函数设计程序。

    副作用,就是修改了某处的东西,比如:

    修改外部变量的值
    IO, 写到磁盘
    UI,修改一个按钮的操作状态
    

    副作用与可变数据和共享状态相关

    var a = 1
    //val a = 1 -> error
    fun count(x:Int){
        a = a +1
        println(x + a)
    }
    
    • val 防御编程思维,更安全可靠
    • 不可变对于复杂的业务逻辑优势会更大

    var 使用场景
    var 一定成都兼容java
    在一些遍历中,使用var 会更加简洁,节省内存
    kotlin底层变量也大量使用了var,针对数据结构,业务中需要大量存储数据var 似乎起了更大作用

    fun cal(list:List<Int>):Int{
        return list.fold(0){res,el->res * el + 1}
    }
    
    fold 底层实现
    
    /**
     * Accumulates value starting with [initial] value and applying [operation] from left to right to current accumulator value and each element.
     */
    public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
        var accumulator = initial
        for (element in this) accumulator = operation(accumulator, element)
        return accumulator
    }
    

    1.4 抽象和高阶函数

    高阶函数---> 以其他函数作为参数或者返回值的函数

    kotlin 中函数类型非常简单,
    如 (int)->Unit
    () -> Unit
    (Int,String) -> Unit
    (errorCode:Int,errMsg:String) -> Unit

    • -> 符号 左边是参数类型,右边是返回值
    • 必须用括号包裹参数类型
    • 返回值 即使是Unit 也必须显示声明

    方法和成员的引用---> 通过两个冒号来实现对于某个类的方法引用,Kotlin 沿用了java8 的习惯

    class Book2(var name:String)
    
    // 沿用java8 语法风格双冒号引用类和成员
        val getBook = ::Book2
        println(getBook("David is good").name)
    

    Book::name 形式

    val bookNames = listOf(
            Book2("David is good"),
            Book2("Json is good")
            ).map(Book2::name)
        println(bookNames)
    

    匿名函数----> 缺省函数名情况下,直接定义一个函数
    fun filterCustom(names:List<String>,test:(name:String)->Boolean){

    }

    匿名函数作为参数

    filterCustom(bookNames,fun(name:String):Boolean{
            return name.contains("David")
        })
    

    1.5 Lambda 语法糖

    java8 中也有类似,可以理解为简化的匿名函数

    如上匿名函数作为参数

        filterCustom(bookNames, { name -> name.contains("David") })
    
    -> 
    
        filterCustom(bookNames) { name -> name.contains("David") }
    
    
    • 一个lambda 表达式 必须用”{}“ 包裹
    • 如果lambda声明了参数部分类型,并且返回值类型支持类型推导,那么Lambda可以省略函数类型声明。
    • 如果lambda声明了函数的类型,lambda参数类型就可以省略
    • 如果lambda 返回值不是Unit,那么默认最后一行表达式类型,就是返回值类型

    lambda 语法糖 it

    listOf(1,2,3).forEach{
            println(it)
        }
    

    Function 类型

    JVM 层设计了Function类型(Function0 ......Function22)兼容java Lambda表达式-----> kotlin.jvm.functions

    每个Function类型都有一个invoke()

    fun bar(int: Int): () -> Unit = { println(int) }
    
    
     //Function 类型 invoke ()
        listOf(7, 8, 9).forEach {
            bar(it)
    --> 不会打印
        }
        listOf(7, 8, 9).forEach {
            bar(it).invoke()
        }
    
        listOf(7, 8, 9).forEach {
            bar(it)()
        }
    

    1.6 函数,lambda 和 闭包

    fun

    • fun 在没有等号,只有花括号的情况下,使我们最常见的代码块函数体,如果返回非Unit 那么必须带有return
    fun foo2(x : Int){ println(x) }
    fun foo3(x : Int,y : Int) :Int {return x * y}
    
    • fun 带有符号,是单表达式函数体。该情况下可以省略return。
      fun sum(x: Int, y: Int) = x + y

    lambda

    • 无论是val 还是 fun,如果是等号+花括号语法,那么构建的就是lambda表达式,lambda的参数在花括号里面声明。所以如果左侧是fun 那么就是lambda 函数体,也必须用() 或者invoke()调用lambda
    val foo5 = { x: Int, y: Int -> x + y }
    fun foo4(x: Int) = { y: Int -> x + y }
    
    // lambda fun val
        foo5.invoke(100,200)
        foo4(1_000).invoke(2_000)
    

    闭包

    匿名函数体,lambda(以及局部函数,object表达式)在语法上都存在”{}“,由花括号包裹内容访问外部环境变量被称为闭包
    ----> "访问外部环境变量的函数" l

    kotlin中闭包不仅能访问外部变量,还能对其修改

    //闭包
        var sum = 0
        listOf(1,2,3).filter{it > 1}.forEach { sum += it }
        println("sum = $sum")
    

    还有一种自运行lambda 语法

    {x:Int -> println(x)}(1_000) 
    

    1.7 “柯里化” 风格,扩展函数

    fun sum2(x: Int, y: Int, z: Int) = x + y + z
    
    fun sum(x:Int)={
        y:Int -> { z:Int -> x+y+z}
    }
    
    
      val testsum1 = sum2(1,2,3)
     val testsum2 = sum(1)(2)(3)
      println("testsum1=${testsum1} testsum2=${testsum2}")
    

    柯里化就是为了简化Lambda演算理论接受多参数,简化多元函数为一元

    Lambda 特殊用法如果一个函数,只有一个参数,并且是函数类型。那么在调用的时候,外面的括号可以省略

    fun omitParenthese(block:()->Unit){
        block()
    }
    
    omitParenthese{
            println("Parenthese is omited")
        }
    

    如果参数不只有一个,而且最后一个参数为函数类型,就可以采用类似柯里化风格调用。

    fun curringLike(current: String, block: (String) -> Unit) {
        block(current)
    }
    
    curringLike("like style") { 
            content ->
            println(content)
        }
    
    等价
        curringLike("like style",
            { content -> println(content) }
        )
    
    
    

    kotlin 允许我们在不修改原有类情况下,给他增加新的方法

    android 中View

    fun View.invisible(){
        visibility = View.INVISIBLE
    }
    
    // 语法演示
    fun main() {
        var views = listOf<View>()
        views.forEach { it.invalidate() }
    
    }
    
    
    
    fun <A, B> Array<A>.corresponds(that: Array<B>, p: (A, B) -> Boolean): Boolean {
        val i = this.iterator()
        val j = that.iterator()
        while (i.hasNext() && j.hasNext()) (
                if (!p(i.next(), j.next())) {
                    return false
                }
                )
        return !i.hasNext() && !j.hasNext()
    }
    
    
    // 扩展函数
        val a = arrayOf(1, 2, 3)
        val b = arrayOf(2, 3, 4)
        val test1 = a.corresponds(b) { x, y -> x + 1 == y }
        val test2 = a.corresponds(b) { x, y -> x + 2 == y }
        println("test1 test2 : $test1 --- $test2")
    

    1.8 面向表达式编程

    语句的作用服务于创建副作用 (就是可以改或者null)
    表达式的目的为了创造新的值。函数式编程中,原则上表达式不允许包含副作用

    Unity 类型: 让函数调用皆为表达式

    java 中Void 对于void ,没有返回值Void 不具有实例

    Unit在kotlin不代表任何信息,用面向对象立即就是一个单例可以写为“()”

    对比 Java8 添加Action<T> 这种函数式接口来解决问题

           Consumer<T> 接收一个参数,返回无结果;
           BiConsumer<T,U> 接收两个参数,返回无结果;
           ObjDoubleConsumer<T> 接收一个object参数和一个double参数,返回无结果;
           ObjIntConsumer<T> 接收一个object参数和一个int参数,返回无结果;
           ObjLongConsumer<T> 接收一个object参数和一个long参数,返回无结果;
    

    符合表达式 try catch

    val res:Int? = try {
            if (result.success){
                jsonDecode(result.response)
            } else null
        }catch (e:JsonDecodeException){
            null
        }
    

    try 也是表达式,返回值由try和cach决定

    枚举和when 提示一下,具体参照官网

    1. kotlin枚举是类实现的
    2. 用when代替if-else

    for 循环表达式
    in 检查成员关系
    范围表达式 "..."

    infix 中缀表达式

    A 中缀方法 B

    定义一个中缀函数,必须满足下面条件:

    • 中缀函数必须是某类型的扩展函数后者成员成员方法
    • 中缀函数只有一个参数
    • kotlin虽然参数有默认,但是中缀函数不能有默认值,否则B会缺失,造成语义破坏
    • 该参数不能是可变参数,我们保持参数始终是1个

    中缀表达式,支持类更贴近自然语言

    class Person{
        infix fun  called(name:String){
            println("My name is $name")
        }
    }
    
    val p = Person()
        p called "David"
    
    

    Kotlin中用varargs关键字表示可变参数,类似java中“..."
    我们可以用*号来传入多个参数

    
    fun printletters(vararg  letters:String,count:Int){
        println("$count letters are $letters")
    }
    
    val letters = arrayOf("a","b","c")
        printletters(*letters,count = 3)
    

    to 返回Pair 键值对,常和map结合在一起

    mapOf(
            1 to "one",
            2 to "two"
        )
    

    类似的还有in,step,downTo,until

    相关文章

      网友评论

        本文标题:Kotlin (一)基础

        本文链接:https://www.haomeiwen.com/subject/eruxnktx.html