代码之家  ›  专栏  ›  技术社区  ›  Djura Marinkov

MyClass和MyClass.this.type之间的区别是什么?如何将两者转换为另一种?

  •  1
  • Djura Marinkov  · 技术社区  · 5 年前

    我的情况与此类似:

    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 type

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

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

    而且你们都提议把Param的定义移到类之外,我完全同意。因此,它的定义将在伴生对象MyClass中

    3 回复  |  直到 5 年前
        1
  •  5
  •   Andrey Tyukin    5 年前

    这个 this.type singleton type 只有一个值,即 this f: X => this.type 作为一个论点肯定是荒谬的,因为 f 可以被替换为 (加上 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: MyClass 这真是个坏主意:

    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)
    

    这个爆炸了一个炸弹 ClassCastException 即使 a b 两者都属于同一类型 A .

        2
  •  2
  •   Astrid    5 年前

    如果你不介意让你的性格 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 MyClass 而不是 Abst 即使它是在 文章摘要 ,但这不是你现在的处境。子类型引用使用的唯一位置是在的定义中 f ,并且由于函数类型在其输出参数上是协变的,所以可以传递任何 f: Double => MyClass 变成 f: Double => Abst[T] 没有问题。

    如果您确实希望引用子类型,请参阅Markus的答案。。。如果你真的想避免 T 作为一个通用参数,事情又变得复杂多了,因为现在您在 属于 T 的定义中的子类型 大都会2 .

        3
  •  2
  •   Markus Appel    5 年前

    要克服此错误消息,您必须使用 F-bounded polymorphism

    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 . 因此,如果 他不是那种人 F .

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

    在实施中,, MyClass 具有 Abst[MyClass, Param] ,这反过来又满足了 F<:文章摘要[F,T] .

    作为中函数的返回类型 Abst 回来 类名 在执行中。


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

    而不是使用 始终建议使用 .

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

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