Kotlin (一)基础

基础语法

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{
    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  Iterable.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,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()
    views.forEach { it.invalidate() }

}


fun  Array.corresponds(that: Array, 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 这种函数式接口来解决问题

       Consumer 接收一个参数,返回无结果;
       BiConsumer 接收两个参数,返回无结果;
       ObjDoubleConsumer 接收一个object参数和一个double参数,返回无结果;
       ObjIntConsumer 接收一个object参数和一个int参数,返回无结果;
       ObjLongConsumer 接收一个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 (一)基础)