Kotlin 核心编程 (17):基础语法 2.1.2

阅读数:1 2019 年 12 月 25 日 15:32

Kotlin核心编程(17):基础语法 2.1.2

(声明函数返回值类型)

内容简介
本书不是一本简单介绍 Kotlin 语法应用的图书,而是一部专注于帮助读者深入理解 Kotlin 的设计理念,指导读者实现 Kotlin 高层次开发的实战型著作。书中深入介绍了 Kotlin 的核心语言特性、设计模式、函数式编程、异步开发等内容,并以 Android 和 Web 两个平台为背景,演示了 Kotlin 的实战应用。
全书共 13 章,分为 4 个部分:
热身篇—Kotlin 基础(第 1~2 章),简单介绍了 Kotlin 设计哲学、生态及基础语法,其中包括 Kotlin 与 Scala、Java 之间的关联与对比,以及 Kotlin 的类型声明的特殊性、val 和 var 的使用、高阶函数的使用、面向表达式编程的使用、字符串的定义与操作等内容;
下水篇—Kotlin 核心(第 3~8 章),深入介绍了面向对象、代数数据类型、模式匹配、类型系统、Lambda、集合、多态、扩展、元编程等 Kotlin 开发核心知识,这是本书的重点,其中涉及很多开发者特别关心的问题,比如多继承问题、模式匹配问题、用代数数据类型抽象业务问题、泛型问题、反射问题等。
潜入篇—Kotlin 探索(第 9~11 章),探索 Kotlin 在设计模式、函数式编程、异步和并发等编程领域的应用,其中包括对 4 大类设计模式、Typeclass 实现、函数式通用结构设计、类型替代异常处理、共享资源控制、CQRS 架构等重点内容的深入剖析;
遨游篇—Kotlin 实战(第 12~13 章),着重演示了 Kotlin 在 Android 和 Web 平台的实战案例,其中涉及架构方式、单向数据流模型、解耦视图导航、响应式编程、Spring 5 响应式框架和编程等内容。

虽然 Kotlin 在很大程度上支持了类型推导,但这并不意味着我们就可以不声明函数返回值类型了。先来看看 Kotlin 如何用 fun 关键字定义一个函数:

复制代码
fun sum(x: Int, y: Int): Int { return x + y }

与声明变量一样,类型信息放在函数名的后面。现在我们把返回类型声明去掉试试:

复制代码
>>> fun sum(x: Int, y: Int) { return x + y }
error: type mismatch: inferred type is Int but Unit was expected

在以上的例子中,因为没有声明返回值的类型,函数会默认被当成返回 Unit 类型,然而实际上返回的是 Int,所以编译就会报错。这种情况下我们必须显式声明返回值类型。

由于一些语言如 Java 没有 Unit 类型,你可能不是很熟悉。不要紧,当前你可以暂时把它当作类似 Java 中的 void。不过它们显然是不同的,Unit 是一个类型,而 void 只是一个关键字,我们会在 2.4.2 节进一步比较两者。

也许你会说,Kotlin 看起来并没有比 Java 强多少嘛,Java 也支持某种程度上的类型推导,比如 Java 7 开始已经支持泛型上的类型推导,Java 10 则进一步支持了“局部变量”的类型推导。

其实,Kotlin 进一步增强了函数的语法,我们可以把{}去掉,用等号来定义一个函数。

复制代码
fun sum(x: Int, y: Int) = x + y // 省略了{}
>>> sum(1, 2)
3

Kotlin 支持这种用单行表达式与等号的语法来定义函数,叫作表达式函数体,作为区分,普通的函数声明则可叫作代码块函数体。如你所见,在使用表达式函数体的情况下我们可以不声明返回值类型,这进一步简化了语法。但别高兴得太早,再来一段递归程序试试看:

复制代码
>>> fun foo(n: Int) = if (n == 0) 1 else n * foo(n - 1)
error: type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly

你可能觉察到了 if 在这里不同寻常的用法——没有 return 关键字。在 Kotlin 中,if 是一个表达式,它的返回值类型是各个逻辑分支的相同类型或公共父类型。

表达式在 Kotlin 中占据了非常重要的地位,我们会在 2.4 节重点介绍这一特性。

我们发现, 当前编译器并不能针对递归函数的情况推导类型。由于像 Kotlin、Scala 这类语言支持子类型和继承,这导致类型系统很难做到所谓的全局类型推导

关于全局类型推导(global type inference),纯函数语言 Haskell 是一个典型的代表,它可以在以上的情况下依旧推导出类型。

所以,在一些诸如递归的复杂情况下,即使用表达式定义函数,我们也必须显式声明类型,才能让程序正常工作。

复制代码
fun foo(n: Int): Int = if (n == 0) 1 else n * foo(n - 1) // 需显式声明返回类型
>>> foo(2)
2

此外,如果这是一个表达式定义的接口方法,显式声明类型虽然不是必需的,但可以在很大程度上提升代码的可读性。

总结

我们可以根据以下问题的提示,来判断是否需要显式声明类型:

  • 如果它是一个函数的参数?
    必须使用。
  • 如果它是一个非表达式定义的函数? 除了返回 Unit,其他情况必须使用。
  • 如果它是一个递归的函数?
    必须使用。
  • 如果它是一个公有方法的返回值?
    为了更好的代码可读性及输出类型的可控性,建议使用。
    除上述情况之外,你可以尽量尝试不显式声明类型,直到你遇到下一个特殊情况。

Kotlin核心编程(17):基础语法 2.1.2

购书地址 https://item.jd.com/12519581.html?dist=jd

评论

发布