Abh*_*kar 2 generics types scala implicit-conversion
我正在通过"Scala for the Impatient"一书中的练习来学习Scala.一个问题是:
给定一个可变
Pair[S, T]类,使用类型约束来定义一个交换方法,如果类型参数相同,则可以调用该方法.
我的代码:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) {
val temp = first
first = second // doesn't compile
second = temp
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码失败,并抱怨编译first和second是不同的类型.好吧,我很好地告诉编译器他们不是.怎么能告诉它闭嘴?
你刚刚告诉类型传递给你的类,因为编译器T和S应该是平等的-你只需要他们的平等,它可以被用来推断实际的证据T,并S正确地当你通过实际的类型(但通用类本身不中) .这并不意味着,T和S是可以互换的.顺便说一句,它不会改变任何东西,但您可以通过定义新做了一个错误的S和T,应该是:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) { //without [S, T] - they are unrelated to original ones, it's whole new names
val temp = first
first = second // doesn't compile
second = temp
}
}
Run Code Online (Sandbox Code Playgroud)
但它仍然无法编译.为什么?试想一下:
def getBoth(implicit ev: T =:= S) = List(first, second)
Run Code Online (Sandbox Code Playgroud)
那么返回类型编译器应该推断出什么?List[T]或List[S].它唯一能做的就是List[Any].因此,同时拥有相同和不同的类型毫无意义.如果你想要不同的名字 - 只需使用就type S = T不需要证据.
什么是在构造函数中具有S和T不同的真实情况,在某些方法中是相同的.这在逻辑上是不正确的(当谈论抽象类型 - 而不是具体的替代).
顺便说一句,=:=不是编译器功能 - 在编译器中没有特殊处理.整个实现是在scala中Predef:
@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }
object =:= {
implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}
Run Code Online (Sandbox Code Playgroud)
所以它只是tpEquals[A]隐含的,它采用类型A并给你A =:= A- 当你需要T =:= U它时,它试图进行这样的转换,这种转换只能用于相同的类型.并检查隐本身的过程,当你经过实际实际上只发生T和U,而不是当你定义它们.
关于您的特定问题:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) {
val temp = first
first = second.asInstanceOf[T]
second = temp.asInstanceOf[S]
}
}
scala> new Pair(5,6)
res9: Pair[Int,Int] = Pair@6bfc12c4
scala> res9.swap
scala> res9.first
res11: Int = 6
Run Code Online (Sandbox Code Playgroud)
或者只是(正如@mz和@Imm建议的那样):
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S, ev2: S =:= T) {
val temp = first
first = second
second = temp
}
}
Run Code Online (Sandbox Code Playgroud)
T =:= S延伸T => S,这隐含地添加功能(甚至作为一个对象)被解释为从隐式转换T到S在Scala中,所以它的工作原理是这两种类型是相等的,这是相当凉爽.