Когда тип возврата требуется для методов в Scala?

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

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

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

12
задан Jesper 27 June 2010 в 11:33
поделиться

1 ответ

В главе 2. Меньше, делай больше книги Programming Scala упоминается:

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

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

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

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

Пример:

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

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

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

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

Поскольку второй метод соединения вызывает первый, ему требуется явный возвращаемый тип 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], потому что возвращал пустой список, но Кен позвонил вывод:

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

19
ответ дан 2 December 2019 в 18:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: