End

Kotlin 朱涛 思维-2 表达式思维 类型 Any Unit Nothing

本文地址


目录

Kotlin 表达式思维

表达式思维

代码案例

val data = null
val i = data ?: 0                           // 如果 data 为 null,则赋值为 0
val j = data ?: 0.also { println("赋值") }   // 如果 data 为 null,则赋值同时打印 log
val k = data ?: throw RuntimeException("空") // 如果 data 为 null,则抛异常

val x = when (data) {
    is Int -> data
    else -> 0
}

val y = try {
    "Kotlin".toInt()
} catch (e: NumberFormatException) {
    println(e)
    0
}

表达式与语句

Kotlin 中,if、when、throw、try-catch 这些语法,都是表达式

表达式对应的另一个概念叫语句

  • 表达式 :Expression,是一段可以产生值的代码
  • 语句 :Statement,是一句不产生值的代码
val a = 1     // statement
val b = 1 + 2 // 1 + 2 是一个表达式,但是对 b 的赋值行为是 statement

fun min(a: Int, b: Int) = if (a > b) b else a // if else 整体是一个表达式
fun cal(): Int = throw NotImplementedError()  // throw xxx 是一个表达式
  • 语句与表达式,可能会出现在同一行代码中,比如 val b = 1 + 2
  • if 等 Kotlin 语法,既可作为语句,也可作为表达式
  • 表达式中可以包含子表达式
  • throw 语句也可以作为表达式

Kotlin 的类型系统

要真正理解 Kotlin 表达式背后的原理,需要了解 Kotlin 的类型系统

Any 与 Any? 与 Object

  • Any 是所有 非空类型 的根类型
  • Any? 是所有 Kotlin 类型的根类型,大致对应 Java 中的 Object 类型
  • AnyAny? 在定义时,并没有明确的继承关系,但它们又存在父子类型的关系
  • 可以将 Any 看作是 Any? 的子类,将 String 看作是 String? 的子类
val a: Any  = ""
val b: Any? = a  // 通过,Any 类型可以赋值给 Any? 类型
val c: Any  = b  // 报错,Any? 类型不可以赋值给 Any 类型

注解 Nullable 与 NotNull

Java 中的 Object 类型与 Kotlin 的 Any? 类型大致对应,但不完全等价。

@Nullable public Object test1() { return null; } // 返回 Object 类型,带可空注解
          public Object test2() { return null; } // 返回 Object 类型,无任何注解
@NotNull  public Object test3() { return 1; }    // 返回 Object 类型,带非空注解

将上面的 Java 代码转换成 Kotlin 以后,会变成这样:

 fun test1(): Any? { return null } // 有可空注解时,Object 类型被换行成了 Any?
 fun test2(): Any? { return null } // 无任何注解时,Object 类型被换行成了 Any?
 fun test3(): Any { return 1 }     // 油非空注解时,Object 类型被换行成了 Any

由此可见,在有了注解修饰以后,Kotlin 就能够识别出到底是 Any,还是 Any?

Unit 与 Unit? 与 Void 与 void

Unit

  • 在 Java 当中,Void 是一个普通的 Java 类(class),void 是一个用于 修饰方法的关键字
  • Kotlin 中的 Unit 兼具 Java 中 Void 和 void 两种功能
    • Unit 也是一个普通的类,只不过 Kotlin 编译器会特殊对待它
    • Unit 也是用来声明方法的返回值的,当 Unit 作为返回值类型时,表明方法没有返回值,此时可以省略 return
fun fun1(): Unit { } 
fun fun2(): Unit { return }
fun fun3(): Unit { return Unit }
fun fun4(): Unit = Unit

Kotlin 的 Unit 与 Java 的 Void 或者 void 并不存在等价的关系,但它们之间确实存在一些概念上的相似性。

Unit?

Kotlin 编译器只会把 Unit 类型当作无需返回值的类型,而其对应的可空类型 Unit? 则不行。所以,Unit? 这个类型其实没有什么实际的应用场景。

Unit? 作为返回值的时候,函数必须要 return 一个值,且返回值的类型只能是 nullUnit 单例Nothing 这三种情况。

fun fun1(): Unit? { }                 // 报错,缺少 return
fun fun2(): Unit? { return }          // 报错,缺少 return

fun fun3(): Unit? { return Unit }     // 通过
fun fun4(): Unit? = Unit              // 通过
fun fun5(): Unit? = null              // 通过
fun fun6(): Unit? = throw Exception() // 通过

Unit 与泛型

当 T 类型作为返回值类型的时候,如果 T 的实际类型为 Unit,可以省略 return。

interface Task<T> {
    fun excute(any: Any): T // 返回值类型
}

class PrintTask: Task<Unit> {
    override fun excute(any: Any) {
        println(any)
        // 可以省略 return
    }
}

Unit 与函数类型

借助 Unit,才有了 Kotlin 中的函数类型。

val f: () -> Unit = {}

Nothing 和 Nothing?

Nothing

  • Any? 是所有的 Kotlin 类型的父类型
  • Nothing 则是所有 Kotlin 类型的 子类型,在函数式编程中,也叫做底类型(Bottom Type)
  • Nothing 可以看作是 Nothing? 子类型

Nothing 与 throw

因为 throw 表达式的返回值是 Nothing 类型,而 Nothing 是所有类型的子类型,所以 throw 表达式可以赋值给任意类型

fun f1(): Int = throw Throwable()
fun f2(): Any = throw Error()
fun f3(): Unit = throw Exception()
fun f4(): Nothing = throw Exception()
fun f5(): Nothing? = throw Exception()

无法构造 Nothing 实例

Nothing 构造函数是私有的,因此无法直接构造它的实例:

public class Nothing private constructor() // Nothing 的构造函数是私有的

这就导致:

  • 当 Nothing 作为函数参数的时候,这个函数将永远 无法被正常调用
    • 除非我们 主动抛出异常,但这没有意义
    • 这在泛型星投影的时候是有一定应用的 -- 我还不知道啥用
  • 当 Nothing 作为函数返回值的时候,这个函数永远 无法正常返回结果
    • 除非我们 主动抛出异常,但这同样会截断程序的后续流程
    • 意味着它后面的语句不再有机会被执行,Kotlin 编译器可据此进行流程分析
fun f1(n: Nothing) = println("哈哈哈") // 参数类型是 Nothing,由于无法构造 Nothing,所以函数体不会执行
fun f2(): Nothing = throw Exception() // 返回值类型是 Nothing,只能主动抛出异常,导致后面的代码无法执行

fun main() {
    f1(throw Exception()) // 主动抛出异常,不报错,但会提示:Unreachable code
    f1(null)              // 报错:Null can not be a value of a non-null type Nothing
    f2()                  // 这个方法里面会抛出异常,所以后面的代码是无法执行的
    println("代码不可触及") // 这里会提示:Unreachable code
}

Nothing?

Nothing? 没有 Nothing 的一系列特性与限制,可以看作是 Nothing 的父类型,也可以看作是一个非常普通的类型。

  • 使用 Nothing? 作为返回值类型的函数是一个普通的函数,可以返回 null 或者是抛出异常
  • 使用 Nothing? 作为参数类型的函数是一个普通的函数,可以传入 null 来调用函数,或者是抛出异常
fun f1(n: Nothing?) = println("哈哈哈") // 参数类型是 Nothing?,可以传入 null
fun f2(): Nothing? = null              // 返回值类型是 Nothing?,可以返回 null
fun f3(): Nothing? = throw Exception() // 返回值类型是 Nothing?,可以抛出异常

fun main() {
    f1(null)               // 传入 null
    val n: Nothing? = f2()
    println("返回值为 $n")  // 正常打印【返回值为 null】
    f3()                  // 这个方法里面会抛出异常,所以后面的代码是无法执行的
    println("代码不可触及") // 不会提示代码有问题
    f1(throw Exception()) // 抛出异常,编译不报错
}

表达式的本质

Kotlin 的类型系统让大部分的语句都变成了表达式,同时也让无返回值的函数有了类型。

  • 表达式是一段可以产生值的代码,而语句则是一句不产生值的代码
  • Kotlin 中的大部分代码都是可以作为表达式的,也即都是有返回值
  • Kotlin 类型系统中的 Unit 和 Nothing,让很多原本无法产生返回值的语句,可以变成表达式
fun main() {
    println("bqt")                     // statement
    val a: Unit = println("bqt")       // 上面的语句变成了表达式,它所产生的值就是 Unit 这个单例

    throw Exception()                  // statement
    val b: Nothing = throw Exception() // 表达式,它所产生的值是 Nothing 类型
    fun test() = throw Exception()     // 表达式,它所产生的值是 Nothing 类型
}

2016-05-12

posted @ 2016-05-12 23:44  白乾涛  阅读(5754)  评论(0编辑  收藏  举报