Scala 中的方法何时需要返回类型? [英] When is a return type required for methods in Scala?

查看:26
本文介绍了Scala 中的方法何时需要返回类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Scala 编译器通常可以推断方法的返回类型,但在某些情况下需要指定返回类型.例如,递归方法需要指定返回类型.

我注意到有时我会收到错误消息重载的方法(方法名)需要返回类型",但这并不是必须始终为重载方法指定返回类型的一般规则(我有一些示例,但我没有得到这个错误).

什么时候需要为一般的方法和特别是重载方法指定返回类型?

解决方案

第 2 章. 输入更少,做更多Programming Scala> 书籍提及:

<块引用>

需要显式类型注释时.

实际上,您必须为以下情况提供显式类型注释:

以下情况下的方法返回值:

  • 当您在方法中显式调用 return 时(甚至在最后).
  • 当一个方法是递归的.
  • 当一个方法被重载并且其中一个方法调用另一个方法时.调用方法需要返回类型注释.
  • 当推断的返回类型比您预期的更通用时,例如 Any.

示例:

//代码示例/TypeLessDoMore/method-nested-return-script.scala//错误:在 upCase 上放置 String 返回类型之前不会编译.def upCase(s: String) = {如果(s.length == 0)return s//错误 - 强制声明 upCase 的返回类型.别的s.toUpperCase()}

<块引用>

重载方法有时可能需要显式返回类型.当一个这样的方法调用另一个方法时,我们必须向执行调用的方法添加一个返回类型,如本例所示.

//代码示例/TypeLessDoMore/method-overloaded-return-script.scala//StringUtil"的第 1 版(有编译错误).//错误:无法编译:第二个joiner"需要一个 String 返回类型.对象 StringUtil {def joiner(strings: List[String], separator: String): String =字符串.mkString(分隔符)def joiner(strings: List[String]) = joiner(strings, " ")//错误}import StringUtil._//导入连接器方法.println( joiner(List("Programming", "Scala")) )

<块引用>

这两个 joiner 方法将一个 List 字符串连接在一起.
第一种方法也接受分隔符字符串的参数.
第二种方法使用单个空格的默认"分隔符调用第一种方法.

如果你运行这个脚本,你会得到以下错误.

... 9: 错误:重载方法连接器需要结果类型def joiner(strings: List[String]) = joiner(strings, "")

<块引用>

由于第二个 joiner 方法调用第一个方法,它需要一个显式的 String 返回类型.它应该是这样的:

def joiner(strings: List[String]): String = joiner(strings, " ")

<小时>

基本上,指定返回类型是一个很好的做法,即使 Scala 可以推断它.

<小时>

Randall Schulz 评论:

<块引用>

根据(我个人的)风格,除了最简单的方法(基本上,没有条件逻辑的单行)之外,我为所有方法都提供了明确的返回类型.

请记住,如果让编译器推断方法的结果类型,它很可能比您想要的更具体.(例如,HashMap 而不是 Map.)

并且由于您可能希望在返回类型中公开最小的接口(例如,请参见 SO 问题),这种推断可能会妨碍.

<小时>

关于最后一种情况(当推断的返回类型比您预期的更通用时"),Ken Bloom 补充:

<块引用>

当您希望编译器验证函数中的代码返回您期望的类型时指定返回类型

(触发比预期的返回类型更通用的错误代码是:

//代码示例/TypeLessDoMore/method-broad-inference-return-script.scala//错误:无法编译.方法实际上返回的是 List[Any],这太宽泛"了.def makeList(strings: String*) = {if (strings.length == 0)列表(0)//#1别的字符串.toList}val list: List[String] = makeList()//错误

,我错误地解释了它和 List[Any] 因为返回的是一个空列表,但是 Ken 把它叫出来了:

<块引用>

List(0) 不会创建包含 0 个元素的列表.
它创建一个包含一个元素(值 0)的 List[Int].
因此,一个条件分支上的 List[Int] 和另一个条件分支上的 List[String] 泛化为 List[Any].
在这种情况下,打字机并没有过于笼统——这是代码中的一个错误.
)

The Scala compiler can often infer return types for methods, but there are some circumstances where it's required to specify the return type. Recursive methods, for example, require a return type to be specified.

I notice that sometimes I get the error message "overloaded method (methodname) requires return type", but it's not a general rule that return types must always be specified for overloaded methods (I have examples where I don't get this error).

When exactly is it required to specify a return type, for methods in general and specifically for overloaded methods?

解决方案

The Chapter 2. Type Less, Do More of the Programming Scala book mentions:

When Explicit Type Annotations Are Required.

In practical terms, you have to provide explicit type annotations for the following situations:

Method return values in the following cases:

  • When you explicitly call return in a method (even at the end).
  • When a method is recursive.
  • When a method is overloaded and one of the methods calls another. The calling method needs a return type annotation.
  • When the inferred return type would be more general than you intended, e.g., Any.

Example:

// code-examples/TypeLessDoMore/method-nested-return-script.scala
// ERROR: Won't compile until you put a String return type on upCase.

def upCase(s: String) = {
  if (s.length == 0)
    return s    // ERROR - forces return type of upCase to be declared.
  else
    s.toUpperCase()
}

Overloaded methods can sometimes require an explicit return type. When one such method calls another, we have to add a return type to the one doing the calling, as in this example.

// code-examples/TypeLessDoMore/method-overloaded-return-script.scala
// Version 1 of "StringUtil" (with a compilation error).
// ERROR: Won't compile: needs a String return type on the second "joiner".

object StringUtil {
  def joiner(strings: List[String], separator: String): String =
    strings.mkString(separator)

  def joiner(strings: List[String]) = joiner(strings, " ")   // ERROR
}
import StringUtil._  // Import the joiner methods.

println( joiner(List("Programming", "Scala")) )

The two joiner methods concatenate a List of strings together.
The first method also takes an argument for the separator string.
The second method calls the first with a "default" separator of a single space.

If you run this script, you get the following error.

... 9: error: overloaded method joiner needs result type
def joiner(strings: List[String]) = joiner(strings, "")

Since the second joiner method calls the first, it requires an explicit String return type. It should look like this:

def joiner(strings: List[String]): String = joiner(strings, " ")


Basically, specifying the return type can be a good practice even though Scala can infer it.


Randall Schulz comments:

As a matter of (my personal) style, I give explicit return types for all but the most simple methods (basically, one-liners with no conditional logic).

Keep in mind that if you let the compiler infer a method's result type, it may well be more specific than you want. (E.g., HashMap instead of Map.)

And since you may want to expose the minimal interface in your return type (see for instance this SO question), this kind of inference might get in the way.


And about the last scenario ("When the inferred return type would be more general than you intended"), Ken Bloom adds:

specify the return type when you want the compiler to verify that code in the function returns the type you expected

(The faulty code which triggers a "more general than expected return type was:

// code-examples/TypeLessDoMore/method-broad-inference-return-script.scala
// ERROR: Won't compile. Method actually returns List[Any], which is too "broad".

def makeList(strings: String*) = {
  if (strings.length == 0)
    List(0)  // #1
  else
    strings.toList
}

val list: List[String] = makeList()  // ERROR

, which I incorrectly interpreted and List[Any] because returning an empty List, but Ken called it out:

List(0) doesn't create a list with 0 elements.
It creates a List[Int] containing one element (the value 0).
Thus a List[Int] on one conditional branch and a List[String] on the other conditional branch generalize to List[Any].
In this case, the typer isn't being overly-general -- it's a bug in the code.
)

这篇关于Scala 中的方法何时需要返回类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆