Глава 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]
.
В этом случае типер не слишком общий - это ошибка в коде .
)