Scala Language
Вывод типа
Поиск…
Локальный вывод типа
Scala имеет мощный механизм ввода-вывода, встроенный в язык. Этот механизм называется «Local Type Inference»:
val i = 1 + 2 // the type of i is Int
val s = "I am a String" // the type of s is String
def squared(x : Int) = x*x // the return type of squared is Int
Компилятор может вывести тип переменных из выражения инициализации. Аналогично, возвращаемый тип методов можно опустить, поскольку они эквивалентны типу, возвращаемому телом метода. Вышеприведенные примеры эквивалентны приведенным ниже объявлениям явного типа:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Тип вывода и дженерики
Компилятор Scala также может выводить параметры типа при вызове полиморфных методов или когда генерируются генерические классы:
case class InferedPair[A, B](a: A, b: B)
val pairFirstInst = InferedPair("Husband", "Wife") //type is InferedPair[String, String]
// Equivalent, with type explicitly defined
val pairSecondInst: InferedPair[String, String]
= InferedPair[String, String]("Husband", "Wife")
Вышеупомянутая форма вывода типа похожа на оператора Diamond , представленную на Java 7.
Ограничения на вывод
Существуют сценарии, в которых тип-вывод Scala не работает. Например, компилятор не может вывести тип параметров метода:
def add(a, b) = a + b // Does not compile
def add(a: Int, b: Int) = a + b // Compiles
def add(a: Int, b: Int): Int = a + b // Equivalent expression, compiles
Компилятор не может вывести возвращаемый тип рекурсивных методов:
// Does not compile
def factorial(n: Int) = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
// Compiles
def factorial(n: Int): Int = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
Предотвращение вывода
Основано на этом сообщении в блоге .
Предположим, у вас есть такой метод:
def get[T]: Option[T] = ???
Когда вы пытаетесь вызвать его без указания общего параметра, Nothing
не выводится, что не очень полезно для реальной реализации (и его результат не пригодится). При следующем растворе NotNothing
контекст связан можно предотвратить с помощью метода без указания ожидаемого типа (в данном примере RuntimeClass
также исключаются за ClassTags
не Nothing
, но RuntimeClass
выводятся):
@implicitNotFound("Nothing was inferred")
sealed trait NotNothing[-T]
object NotNothing {
implicit object notNothing extends NotNothing[Any]
//We do not want Nothing to be inferred, so make an ambigous implicit
implicit object `\n The error is because the type parameter was resolved to Nothing` extends NotNothing[Nothing]
//For classtags, RuntimeClass can also be inferred, so making that ambigous too
implicit object `\n The error is because the type parameter was resolved to RuntimeClass` extends NotNothing[RuntimeClass]
}
object ObjectStore {
//Using context bounds
def get[T: NotNothing]: Option[T] = {
???
}
def newArray[T](length: Int = 10)(implicit ct: ClassTag[T], evNotNothing: NotNothing[T]): Option[Array[T]] = ???
}
Пример использования:
object X {
//Fails to compile
//val nothingInferred = ObjectStore.get
val anOption = ObjectStore.get[String]
val optionalArray = ObjectStore.newArray[AnyRef]()
//Fails to compile
//val runtimeClassInferred = ObjectStore.newArray()
}