27

我有一个应该返回 Int 的方法。我试图理解为什么 Eclipse 不会让我编译它,即使在 if 语句中我看起来很明显我确实返回了一个 Int。有什么我很明显的遗漏吗?在继续编写更多代码之前,我试图了解 Scala 的这一方面。

这是方法:

def contains1(sfType: TokenType): Int = {
     if (Tokens.KEYWORDS.contains(sfType)) {
      val retVal = TokenTypes.RESERVED_WORD
    }
  }

Eclipse 在第 2 行抱怨 --- '类型不匹配; 找到:所需单位:Int"

TokenTypes is - public abstract interface org.fife.ui.rsyntaxtextarea.TokenTypes and RESERVED_WORD is - public static final int RESERVED_WORD = 6;

我在这里读过这篇文章:发现:所需单位:Int - 如何纠正这个?并试图在发布之前解决问题,但我仍然不知所措。

编辑:该方法应该返回一个 Int 并且我输入了错误的返回类型。我的问题还是一样。Eclipse 仍然抱怨。

4

4 回答 4

88

I'll first explain what type Unit is, just in case. Even if you know already, other people having the same kind of problem might well not know it.

Type Unit is similar to what is known in C or Java as void. In those languages, that means "this does not return anything". However, every method in Scala returns a value.

To bridge the gap between every method returning something and not having anything useful to return, there's Unit. This type is an AnyVal, which means it isn't allocated on the heap, unless it gets boxed or is the type of a field on an object. Furthermore, it has only one value, whose literal is (). That is, you could write this:

val x: Unit = ()

The practical effect of this is that, when a method "returns" Unit, the compiler doesn't have to actually return any value, since it already knows what that value is. Therefore, it can implement methods returning Unit by declaring them, on the bytecode level, as being void.

Anyway, if you don't want to return anything, you return Unit.

Now let's look at the code given. Eclipse says it returns Unit, and, in fact, Eclipse is correct. However, most people would actually make the error of having that method return AnyVal or Any, not Unit. See the following snippet for an example:

scala> if (true) 2
res0: AnyVal = 2

So, what happened? Well, when Scala finds an if statement, it has to figure out what is the type returned by it (in Scala, if statements return values as well). Consider the following hypothetical line:

if (flag) x else y

Clearly, the returned value will be either x or y, so the type must be such that both x and y will fit. One such type is Any, since everything has type Any. If both x and y were of the same type -- say, Int -- then that would also be a valid return type. Since Scala picks the most specific type, it would pick Int over Any.

Now, what happens when you don't have an else statement? Even in the absence of an else statement, the condition may be false -- otherwise, there would be no point in using if. What Scala does, in this case, is to add an else statement. That is, it rewrites that if statement like this:

if (true) 2 else ()

Like I said before: if you do not have anything to return, return Unit! That is exactly what happens. Since both Int and Unit are AnyVal, and given that AnyVal is more specific than Any, that line returns AnyVal.

So far I have explained what others might have seen, but not what happens in that specific code in the question:

if (Tokens.KEYWORDS.contains(sfType)) {
  val retVal = TokenTypes.RESERVED_WORD
}

We have seen already that Scala will rewrite it like this:

if (Tokens.KEYWORDS.contains(sfType)) {
  val retVal = TokenTypes.RESERVED_WORD
} else ()

We have also seen that Scala will pick the most specific type between the two possible results. Finally, Eclipse tells use that the return type is Unit, so the only possible explanation is that the type of this:

  val retVal = TokenTypes.RESERVED_WORD

is also Unit. And that's precisely correct: statements that declare things in Scala have type Unit. And, so, by the way, do assignments.

The solution, as others have pointed out, is to remove the assignment and add an else statement also returning an Int:

def contains1(sfType: TokenType): Int =
  if (Tokens.KEYWORDS.contains(sfType)) TokenTypes.RESERVED_WORD
  else -1

(note: I have reformatted the method to follow the coding style more common among Scala programmers)

于 2013-01-09T02:30:53.843 回答
10

我有一种感觉你需要改变你的方法看起来像以下之一

def contains1(sfType: TokeType): Int = {
  if (Tokens.KEYWORDS.contains(sfType))
    TokenTypes.RESERVED_WORD
  else 
    -1
}

def contains1(sfType: TokenType) = if (Tokens.KEYWORDS.contains(sfType)) TokenTypes.RESERVED_WORD else -1
于 2013-01-07T22:47:05.980 回答
8

在 scala 中没有单一的if语句

由于每个表达式在求值时都必须返回一个值(它可以是一个空值,Unit类型为超类型。ifelse

在您的代码中,您Intif分支返回 a ,但else缺少该分支。

更新

正如其他答案中正确说明的那样:

单个if表达式返回其中唯一的替代方案,对于原始帖子,它是赋值的返回值,它是()类型Unit

于 2013-01-07T22:46:36.730 回答
4

Eclipse 认为如果“if”块为假,即Tokens.KEYWORDS.contains(sfType)is false,那么返回类型确实是Unit,这就是问题所在。

于 2013-01-09T02:39:27.057 回答