《Kotlin核心编程》 ——2.1.2 声明函数返回值类型
2.1.2 声明函数返回值类型
虽然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,其他情况必须使用。
如果它是一个递归的函数?
必须使用。
如果它是一个公有方法的返回值?
为了更好的代码可读性及输出类型的可控性,建议使用。
除上述情况之外,你可以尽量尝试不显式声明类型,直到你遇到下一个特殊情况。
- 点赞
- 收藏
- 关注作者
评论(0)