为什么this.type不能用于新实例

时间:2009-04-22 00:50:02

标签: scala

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

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

为什么新案例类与this.type不匹配?

如果我将this.type更改为Base.new_with_changes中的Base并且Derived.new_with_changes中的Derived可以正常工作,但似乎它错过了this.type的精确性。

编辑:问题的真正意图是为什么在Scala中没有一种等效的方式来声明down的调用者执行downcast,就像this.type那样,但是对于一般类型。我不认为这很容易,但它会很好。

2 个答案:

答案 0 :(得分:9)

this.type是此特定实例的唯一类型。它是单例类型 - 与同一类的任何其他实例不同的类型。这工作

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

但这不是

class Foo { def f : this.type = new Foo}
经常不需要this.type,但它可以用来表达一些不能表达的约束

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

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

答案 1 :(得分:7)

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

现在如果您实际上在任何地方使用this.type的singleton属性,这将使您匆忙陷入困境。但是如果你想做的就是获得协变的返回类型而不用输入它们的麻烦,只有这个地方巨大的丑陋演员的小缺点:

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