学习 Scala 的通用边界有点复杂。我知道:
T : Tr
-T
具有类型 of Tr
,意味着它实现了一个特征 Tr
T <: SuperClass
-T
是的子类SuperClass
T :> ChildClass
-T
是超类ChildClass
但是,还有更多的运算符:
<%
和%>
=:=
<:<
和>:>
<%
和%>
<%<
和>%>
我读到了它们,但正如我所说,没有可理解的解释。你能说得清楚一点吗?
学习 Scala 的通用边界有点复杂。我知道:
T : Tr
-T
具有类型 of Tr
,意味着它实现了一个特征 Tr
T <: SuperClass
-T
是的子类SuperClass
T :> ChildClass
-T
是超类ChildClass
但是,还有更多的运算符:
<%
和%>
=:=
<:<
和>:>
<%
和%>
<%<
和>%>
我读到了它们,但正如我所说,没有可理解的解释。你能说得清楚一点吗?
我使用了一些类型约束:
最简单的是<:
和>:
。这些是类型层次结构的简单界限。
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))
}
在范围内的某个地方应该有 type 的隐含值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
。但是只有当 trait 被实例化时才能调用该方法Int
。
存在类似的用例<:<
。但是这里我们没有“等于”约束,而是“小于”(如T<: T2
)。
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T <:< MyParentWithInterestingMethods)
}
同样,该方法只能调用MyParentWithInterestingMethods
.
Then<%<
与 非常相似,但是当类型不是泛型参数时<%
,它的使用方式与— 作为隐式参数相同。<:<
它转换为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
...
}
}