Когда требуется возвращаемый тип для методов в Scala? - PullRequest
11 голосов
/ 27 июня 2010

Компилятор Scala часто может определять типы возвращаемых данных для методов, но в некоторых случаях требуется указать тип возвращаемого значения. Например, для рекурсивных методов требуется указать тип возвращаемого значения.

Я замечаю, что иногда я получаю сообщение об ошибке «перегруженный метод (имя метода) требует возвращаемого типа», но это не общее правило, что возвращаемые типы всегда должны указываться для перегруженных методов (у меня есть примеры, где я не получаю это ошибка).

Когда именно требуется указывать тип возвращаемого значения для методов в целом и, в частности, для перегруженных методов?

1 Ответ

18 голосов
/ 27 июня 2010

Глава 2. Напечатайте меньше, сделайте больше из Программирование Scala Книга упоминает:

Когда требуются явные аннотации типов.

В практическом плане вы должны предоставить явные аннотации типов для следующих ситуаций:

Метод возвращает значения в следующих случаях:

  • Когда вы явно вызываете return в методе (даже в конце).
  • Когда метод рекурсивный.
  • Когда метод перегружен и один из методов вызывает другой. Вызывающему методу требуется аннотация возвращаемого типа.
  • Когда предполагаемый тип возврата будет более общим, чем вы предполагали, например, Any.

Пример:

// 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()
}

Перегруженные методы иногда могут требовать явного возвращаемого типа. Когда один такой метод вызывает другой, мы должны добавить тип возврата к тому, который выполняет вызов, как в этом примере.

// 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")) )

Два joiner метода объединяют List строк вместе.
Первый метод также принимает аргумент для строки-разделителя.
Второй метод вызывает первый с разделителем «по умолчанию» для одного пробела.

Если вы запустите этот скрипт, вы получите следующую ошибку.

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

Поскольку второй метод joiner вызывает первый, для него требуется явный тип возврата String. Это должно выглядеть так:

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

По сути, указание типа возвращаемого значения может быть хорошей практикой, даже если Scala может определить его .


Рэндалл Шульц комментарии:

В качестве (моего личного) стиля я даю явные типы возврата для всех, кроме самых простых методов (в основном, однострочников без условной логики).

Имейте в виду, что если вы позволите компилятору выводить тип результата метода, он может быть более конкретным, чем вы хотите. (Например, HashMap вместо Map.)

И, так как вы можете указать минимальный интерфейс в вашем типе возвращаемого значения (см., Например, SO вопрос ), этот тип вывода может помешать.


И о последнем сценарии («Когда предполагаемый тип возврата будет более общим, чем вы предполагали»), Кен Блум добавляет:

укажите тип возвращаемого значения, когда вы хотите, чтобы компилятор проверил, что код в функции возвращает ожидаемый вами тип

(Неисправный код, который вызывает "более общий, чем ожидалось, тип возвращаемого значения:

// 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

, который я неправильно истолковал и List [Any], потому что возвращал пустой List, но Кен вызвал его:

List(0) не создает список с 0 элементами.
Создает List[Int], содержащий один элемент (значение 0).
Таким образом, List[Int] в одной условной ветви и List[String] в другой условной ветви обобщаются до List[Any].
В этом случае типер не слишком общий - это ошибка в коде .
)

...