14

我希望能够使用 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

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

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

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

4

2 回答 2

9

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
于 2009-04-22T02:19:36.043 回答
8

[注意:我不建议你这样做。] 你很有可能完成你想要的。对 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
于 2009-04-26T16:39:52.310 回答