【问题标题】:Why cannot this.type be used for new instances为什么 this.type 不能用于新实例
【发布时间】:2010-10-20 23:48:19
【问题描述】:

我希望能够使用 this.type 来定义一个方法,该方法可以创建不可变案例类的新实例。像这样的:

trait Expression
{
  def left : Expression
  def right : Expression

  def new_with_changes(l : Expression, r : Expression) : this.type
}

case class Derived(left : Expression, right : Expression)
{
  def new_with_changes(l : Expression, r : Expression) : this.type =
  {
    new Derived(left, right)
  }
}

不幸的是,编译器抱怨

test.scala:13: error: type mismatch;
 found   : Derived
 required: Derived.this.type
    new Derived(left, right)
    ^
one error found

新的case类怎么和this.type不匹配?

如果我在 Base.new_with_changes 中将 this.type 更改为 Base 并在 Derived.new_with_changes 中将 Derived 更改为有效,但它似乎错过了 this.type 的优点。

编辑:问题的真正意图是为什么在 Scala 中没有一种等效的方式来声明向下的调用者执行向下转换,这与 this.type 所做的方式非常相似,但对于一般类型。我不认为这很容易,但会很好。

【问题讨论】:

    标签: scala


    【解决方案1】:

    this.type 是这个特定实例的唯一类型。它是一个单例类型 - 与同一类的任何其他实例不同的类型。这行得通

    class Foo { def f : this.type = this}
    

    但这不是

    class Foo { def f : this.type = new Foo}
    

    this.type 并不经常需要,但它可以用来表达一些其他方式无法表达的约束

    例如,这里的 Inner 类表示每个实例的外部方法将返回它所来自的特定 Outer 实例。

    scala> class Outer{ class Inner { def outer : Outer.this.type = Outer.this}; def f(x : Inner) = println("ok")}
    defined class Outer
    
    scala> val o1 = new Outer
    o1: Outer = Outer@13c1b69
    
    scala> val o2 = new Outer
    o2: Outer = Outer@1a3f178
    
    
    scala> val in1 = new o1.Inner
    in1: o1.Inner = Outer$Inner@627b5c
    
    scala> val in2 = new o2.Inner
    in2: o2.Inner = Outer$Inner@158c3b7
    
    scala> val o3 = in1.outer
    o3: o1.type = Outer@13c1b69
    
    scala> o1.f(new o3.Inner)  
    ok
    
    scala> o1.f(new o2.Inner)
    <console>:8: error: type mismatch;
     found   : o2.Inner
     required: o1.Inner
           o1.f(new o2.Inner)
    

    这篇文章有另一个很好的例子,使用 this.type 来启用跨子类边界的方法链接:http://scalada.blogspot.com/2008/02/thistype-for-chaining-method-calls.html

    scala>   class A { def method1: this.type = this }
    defined class A
    
    scala>   class B extends A { def method2: this.type = this }
    defined class B
    
    scala> val b = new B
    b: B = B@15cb235
    
    scala> b.method1.method2
    res3: b.type = B@15cb235
    

    【讨论】:

    • 如果有一种方法可以做我想做的事,除了 this.type 之外,可以声明一个基类方法返回调用者会向下转换的子类型实例,与 this.type 的调用者代码一样。
    【解决方案2】:

    [注意:我不建议您这样做。]您很有可能实现您想要的。对 this.type 的强制转换是一个谎言,但是 JVM 不知道这一点并且不能抛出异常,因为单例类型是一个 scala 概念。

    现在,如果您实际上在任何地方使用 this.type 的单例属性,这将很快给您带来麻烦。但是,如果您想要做的只是获得协变返回类型而无需键入它们的所有麻烦,只有到处都是巨大的丑陋演员的小缺点:

    trait Expression
    {
      def left : Expression
      def right : Expression
    
      def new_with_changes(l : Expression, r : Expression) : this.type
    }
    
    case class Derived1(left : Expression, right : Expression) extends Expression {
      def new_with_changes(l : Expression, r : Expression) =
        Derived1(left, right).asInstanceOf[this.type]
    
      def foo() = "Derived1"
    }
    
    case class Derived2(left : Expression, right : Expression) extends Expression {
      def new_with_changes(l : Expression, r : Expression) =
        Derived2(left, right).asInstanceOf[this.type]
    
      def bar() = "Derived2"  
    }
    

    在行动中:

    scala> Derived1(Derived1(null,null), null)
    res0: Derived1 = Derived1(Derived1(null,null),null)
    
    scala> res0.new_with_changes(res0, null).bar
    <console>:6: error: value bar is not a member of Derived1
           res0.new_with_changes(res0, null).bar
                                             ^
    
    scala> res0.new_with_changes(res0, null).foo
    res2: java.lang.String = Derived1
    
    scala> Derived2(Derived2(null, null), null)
    res3: Derived2 = Derived2(Derived2(null,null),null)
    
    scala> res3.new_with_changes(null, res3).foo
    <console>:6: error: value foo is not a member of Derived2
           res3.new_with_changes(null, res3).foo
                                             ^
    
    scala> res3.new_with_changes(null, res3).bar
    res6: java.lang.String = Derived2
    

    【讨论】:

    • 哈克,但它有效。如果我们能在语言中加入这样的东西,那就太棒了。
    猜你喜欢
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多