Простейшим являются <:
и >:
. Это простые оценки иерархии типов.
trait A
trait B extends A
trait C extends B
то для метода
def doSomething[T >:C <:B](data:T)
либо B
или C
может быть замещен вместо T
Затем введите ограничения, которые включают в себя добавление неявного параметра к методу.
def doSmth1[T: MyTypeInfo] (data:T)
переписывается во время компиляции, как
def doSmth1[T] (data:T)(implicit ev: MyTypeInfo[T])
тогда
def doSmth2[T <% SomeArbitratyType] (data:T)
переписывается в виде
def doSmth2[T] (data:T)(implicit ev: T => SomeArbitratyType)
Оба методов можно назвать, если в объеме существует экземпляр, который соответствует неявный параметр. Если нет соответствующего экземпляра, компилятор выдает ошибку.
Ссылка на просмотр (<%
) требует неявного преобразования, которое преобразует T
в экземпляр другого типа (SomeArbitratyType
).
Более мощный использует «классы типов». Внутри экземпляра класса типа можно указать множество полезных методов, которые могут иметь дело с типом T
. В частности, можно применить метод преобразования и получить аналогичный результат в виде границ представления.
Примеры:
trait MyTypeInfo[T] {
def convertToString(data:T):String
}
def printlnAdv[T : MyTypeInfo](data:T) {
val ev = implicitly[MyTypeInfo[T]]
println(ev.convertToString(data))
}
где-то в объеме должны быть неявным значением типа MyTypeInfo[T]
:
implicit val doubleInfo = new MyTypeInfo[Double] {
def convertToString(data:Double):String = data.toString
}
или
implicit def convertToString(data:T):String
def printlnAdv[T <% String](data:T) {
val conversionResult = data : String
println(conversionResult)
}
где-то в объеме должен быть неявной функцией :
implicit def convertDoubleToString(data:Double):String = data.toString
Следующие странные символы: =:=
и <:<
. Они используются в методах, которые хотят гарантировать, что тип имеет некоторое свойство. Конечно, если вы объявите общий параметр, то достаточно указать <:
и >:
, чтобы указать тип. Однако что делать с типами, которые не являются общими параметрами? Например, общий параметр охватывающего класса или некоторый тип, который определен в другом типе. Символы здесь помогают.
trait MyAlmostUniversalTrait[T] {
def mySpecialMethodJustForInts(data:T)(implicit ev:T =:= Int)
}
Характеристика может использоваться для любых типов T
. Но метод можно вызвать только в том случае, если этот признак создается для Int
.
Подобный прецедент существует для <:<
. Но здесь мы не имеем «равных» ограничений, но «меньше» (например, T<: T2
).
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T <:< MyParentWithInterestingMethods)
}
Опять метод может только можно назвать для типов, которые являются потомками MyParentWithInterestingMethods
.
Тогда <%<
очень похож на <%
, однако она используется так же, как <:<
- как неявный параметр, если тип не является универсальным параметром. Это дает преобразование в T2
:
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T <%< String) {
val s = data:String
...
}
}
ИМХО <%<
можно спокойно игнорировать. И один может просто объявить требуемую функцию преобразования:
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T => String) {
val s = data:String
...
}
}
есть хорошее объяснение на виде и контекст оценку на сайте http://docs.scala-lang.org/tutorials/FAQ/context лестницы и-вид-bounds.html –