MyClass和MyClass.this.type之间的区别是什么?如何将它们转换为另一个



我有一个类似的情况:

trait Abst{
type T
def met1(p: T) = p.toString
def met2(p: T, f: Double=>this.type){
val v = f(1.0)
v.met1(p)
}
}
class MyClass(x: Double) extends Abst{
case class Param(a:Int)
type T = Param
val s = met2(Param(1), (d: Double) => new MyClass(d))
}

它不会显示错误,直到我运行它,然后它说:

类型不匹配;已找到:MyClass,必需:MyClass.this.type

我也尝试了一个具有泛型类型的解决方案,但后来我对此有冲突。T不同于v.T.

所以我只需要克服上面的错误信息,如果可能的话?


更新

因此,事实证明this.type是该单个实例的singleton类型。我在一篇评论中建议使用

val s = met2(Param(1), (d: Double) => (new MyClass(d)).asInstanceOf[this.type])

所以,只要有人对此发表评论,我就知道它有多丑陋,只是感兴趣的是它有多不安全?

此外,你们都建议将Param的定义转移到课堂之外,我绝对同意。因此它的定义将在伴随对象MyClass 中

this.type是由一个值(即this)占据的单例类型。因此,接受类型为f: X => this.type的函数作为自变量是毫无意义的,因为f的每次调用都可以被this取代(加上f执行的副作用)。

以下是一种迫使代码以最小的更改进行编译的方法:

trait Abst { self =>
type T
def met1(p: T) = p.toString
def met2(p: T, f: Double => Abst { type T = self.T }){
val v = f(1.0)
v.met1(p)
}
}
case class Param(a:Int)
class MyClass(x: Double) extends Abst {
type T = Param
val s = met2(Param(1), (d: Double) => new MyClass(d))
}

但老实说:不要这样做。也不要做任何F界的事情,它可能会以一团混乱告终,尤其是如果你不熟悉这种模式的话。相反,重构你的代码,这样你就不会有任何自我引用的螺旋


更新

说明为什么告诉编译器(new MyClass(d))是其他this: MyClassthis.type类型是一个非常糟糕的主意:

abstract class A {
type T
val x: T
val f: T => Unit
def blowup(a: A): Unit = a.asInstanceOf[this.type].f(x)
}
object A {
def apply[X](point: X, function: X => Unit): A = new A {
type T = X
val x = point
val f = function
}
}
val a = A("hello", (s: String) => println(s.size))
val b = A(42, (n: Int) => println(n + 58))
b.blowup(a)

尽管ab都属于A类型,但这会导致ClassCastException爆炸。

如果您不介意让特性将T作为通用参数,这是一个相当简单明了的等效解决方案:

trait Abst[T]{
def met1(p: T) = p.toString
def met2(p: T, f: Double=>Abst[T]){
val v = f(1.0)
v.met1(p)
}
}
case class Param(a:Int)
class MyClass(x: Double) extends Abst[Param]{
val s = met2(Param(1), (d: Double) => new MyClass(d))
}

我认为这是等价的,因为met2使用超类型而不是子类型不会丢失任何信息。引用特征中的子类型的经典用例是,例如,有一个方法,即使在Abst中定义了它,您也希望返回MyClass而不是Abst,但这不是您所处的情况。唯一使用子类型引用的地方是在f的定义中,由于函数类型的输出参数是协变的,因此可以将任何f: Double => MyClass传递到f: Double => Abst[T]中,而不会出现问题。

如果您确实想引用子类型,请参阅Markus的答案。。。如果你真的想避免T是一个通用参数,事情会再次变得复杂得多,因为现在AbstTmet2定义中的子类型的T之间存在潜在的冲突。

要克服此错误消息,必须使用F-有界多态性

你的代码看起来有点像这样:

trait Abst[F <: Abst[F, T], T]{ self: F =>
def met1(p: T): String = p.toString
def met2(p: T, f: Double => F): String = {
val v = f(1.0)
v.met1(p)
}
}
case class Param(a:Int)
class MyClass(x: Double) extends Abst[MyClass, Param] {
val s = met2(Param(1), (d: Double) => new MyClass(d))
}

说明:

在特征或类定义中使用self: F =>会约束this的值。因此,如果this的类型不是F,您的代码将不会编译。

我们使用了一个循环类型约束F:F <: Abst[F, T]。尽管违反直觉,编译器并不介意。

在实现MyClass中,我们用Abst[MyClass, Param]扩展MyClass,从而满足F <: Abst[F, T]

现在,您可以在Abst中使用F作为函数的返回类型,并在实现中让MyClass返回MyClass


你可能认为这个解决方案很丑陋,如果你这样做了,那么你是对的。

不使用F-有界多态性,始终建议使用类型类来实现特定多态性

你可以在我之前提供的链接中找到更多关于它的信息。

真的,读一读吧。它将永远改变你对泛型编程的看法。

我希望这能有所帮助。

相关内容

最新更新