在Scala中,所有抽象类型在擦除后具有相同的类型。
在Scala中,所有抽象类型在擦除后具有相同的类型。
以下代码无法编译,因为两个乘法操作符在类型擦除后具有相同的类型:(f: Object)Object
。我了解类型擦除,但是我看到的所有情况都会擦除泛型类型,例如List[Int]
或List[String]
,正如在Scala double definition (2 methods have the same type erasure)中所回答的那样。我该如何让Scala将不同类型的XxxT类型区分开来?
trait AbstractTypes { type ScalarT type VectorT abstract class Operators(u: VectorT) { def *(f: ScalarT): VectorT def *(v: VectorT): VectorT } }
在Scala中,所有的抽象类型在擦除之后具有相同的类型。这意味着,无论抽象类型的具体实现是什么,它们在运行时都会被擦除为相同的类型。
上面给出的代码展示了一个使用抽象类型的示例。在这个示例中,有一个抽象类型`ScalarT`和`VectorT`,并且定义了一个抽象类`Operators`,它接受一个`VectorT`类型的参数`u`。`Operators`类中有两个方法`*`,一个接受一个`ScalarT`类型的参数`f`,另一个接受一个`VectorT`类型的参数`v`。在第二个方法中,使用了`DummyImplicit`来解决擦除导致参数类型相同的问题。
`DummyImplicit`是一个特殊的隐式值,它不会被实际使用,只是为了在编译时提供一个类型,以解决擦除后参数类型相同的问题。如果需要更多的方法重载,并且它们的擦除结果是相同的类型,可以使用多个`DummyImplicit`。
在上述示例中,通过使用`DummyImplicit`作为第二个方法的隐式参数,可以实现方法重载,即使在擦除之后参数类型相同。
总结起来,Scala中抽象类型在擦除之后会具有相同的类型,为了解决擦除导致的参数类型相同的问题,可以使用`DummyImplicit`来提供类型,并实现方法的重载。这样,即使在擦除之后,方法的参数类型仍然是不同的。
在Scala中,所有的抽象类型在擦除之后都具有相同的类型。上述代码中使用了一个技巧,通过将参数声明为按名称调用的方式来定义第二个方法:
trait AbstractTypes { type ScalarT type VectorT abstract class Operators(u: VectorT) { def *(f: ScalarT): VectorT def *(v: => VectorT): VectorT } } object ConcreteTypes extends AbstractTypes { type ScalarT = Double type VectorT = Seq[Double] class ConcreteOperators(u: Seq[Double]) extends Operators(u) { def *(f: Double): Seq[Double] = u.map(_ * f) def *(v: => Seq[Double]): Seq[Double] = (u zip v).map { case (a, b) => a * b } } } new ConcreteTypes.ConcreteOperators(Seq(2.0, 3.0, 5.0)) * 7.0 Seq[Double] = List(14.0, 21.0, 35.0) new ConcreteTypes.ConcreteOperators(Seq(2.0, 3.0, 5.0)) * Seq(1.0, 2.0, 3.0) Seq[Double] = List(2.0, 6.0, 15.0)
这是一个有趣的技巧,但它稍微改变了语义。当您使用函数而不是值时,例如,每次使用都会再次调用该函数。
问题的出现原因是,当我们使用抽象类型VectorT时,它在擦除之后被擦除为Seq[Double]。因此,当我们在Operators类中定义一个接受VectorT类型参数的方法时,实际上它接受的是Seq[Double]类型参数。这导致在调用该方法时可能会出现类型不匹配的问题。
解决方法是使用类型参数而不是抽象类型。通过将VectorT类型参数化为T,我们可以确保在擦除之后仍然具有正确的类型。修改后的代码如下:
trait AbstractTypes[T] { type ScalarT abstract class Operators(u: T) { def *(f: ScalarT): T def *(v: => T): T } } object ConcreteTypes extends AbstractTypes[Seq[Double]] { type ScalarT = Double class ConcreteOperators(u: Seq[Double]) extends Operators(u) { def *(f: Double): Seq[Double] = u.map(_ * f) def *(v: => Seq[Double]): Seq[Double] = (u zip v).map { case (a, b) => a * b } } } new ConcreteTypes.ConcreteOperators(Seq(2.0, 3.0, 5.0)) * 7.0 Seq[Double] = List(14.0, 21.0, 35.0) new ConcreteTypes.ConcreteOperators(Seq(2.0, 3.0, 5.0)) * Seq(1.0, 2.0, 3.0) Seq[Double] = List(2.0, 6.0, 15.0)
通过使用类型参数,我们可以确保在擦除之后仍然具有正确的类型,并且不会出现类型不匹配的问题。