`*.this` 在 Scala 中有什么用?

What is the use of `*.this` in Scala?

class S {
  case class A(a: Int)
}

abstract class R(val s: S) {
  type T1 = R.this.s.A
  type T2 = s.A
  implicitly[T1 =:= T2] // compiles

  type T3 = R.this.type
  type T4 = this.type
  implicitly[T3 =:= T4] // compiles

  val v1 = R.this //  v1 == `this`
  val v2 = R.this.s // v2 == `s`
}

看起来 .this 部分没有任何效果。提出一个具体的问题: 你什么时候使用 .this ?

一条规则是 Scala 从不推断单例类型 this.type。比如首先考虑它的力学

scala> trait Foo {
     |   type T
     |   def f() = this // we left out the return type to see what Scala will infer
     | }
// defined trait Foo

scala> new Foo { type T = String }
val res0: Foo{T = String} = anon@6d3ad37a

scala> res0.f()
val res1: Foo = anon@6d3ad37a

请注意 res1 如何具有 return 类型的 Foo 而不是 Foo { type T = String },因此我们丢失了一些类型信息

scala> val x: res1.T = ""
1 |val x: res1.T = ""
  |                ^^
  |                Found:    ("" : String)
  |                Required: res1.T

注意编译器不知道res1.T实际上是一个String。所以编译器没有推断出单例类型 this.type ,它会包含所有类型信息,包括实例化到 T 的类型成员

scala> trait Foo {
     |   type T
     |   def f(): this.type = this
     | }
// defined trait Foo

scala> new Foo { type T = String }
val res2: Foo{T = String} = anon@7d381eae

scala> res2.f()
val res3: Foo{T = String} = anon@7d381eae

scala> val x: res3.T = ""
val x: res3.T = ""

请注意,在我们显式声明单例 return 类型 this.type 之后,编译器如何知道 TString.

这是另一个机械示例,说明由于编译器未推断单例类型而发生的情况this.type

scala> trait Foo {
     |   def f() = this // let inference do its thing
     | }
// defined trait Foo

scala> trait Bar {
     |   def g() = 42
     | }
// defined trait Bar

scala> trait Bar extends Foo {
     |   def g(): Int = 42
     | }
// defined trait Bar

scala> new Bar {}
val res5: Bar = anon@6a9a6a0c

scala> res5.f()
val res6: Foo = anon@6a9a6a0c

scala> res6.g()
1 |res6.g()
  |^^^^^^
  |value g is not a member of Foo

请注意 f() 调用是如何输入 Foo 而不是预期的 Bar。另一方面,如果我们提供显式单例 return type this.type then

scala> trait Foo {
     |   def f(): this.type = this
     | }
// defined trait Foo

scala> trait Bar extends Foo {
     |   def g(): Int = 42
     | }
// defined trait Bar

scala> new Bar {}
val res7: Bar = anon@4707d60a

scala> res7.f()
val res8: Bar = anon@4707d60a

scala> res8.g()
val res9: Int = 42

我们看到 f() 呼叫类型为 Bar

这些是原理,但实际应用呢?我知道的两个用途是:

  • 支持fluent编程风格
  • 支持 of type class instances (see also Scala 3 summon)

它对内部 类 很重要。例如

class Outer(val x: Int) {
  class Inner(val x: Int) {
    def outer_this = Outer.this.x
    def inner_this = this.x // or just x
  }
}

val outer = new Outer(0)
val inner = new outer.Inner(1)
println(inner.outer_this) // 0
println(inner.inner_this) // 1

每个 Outer.Inner 实例都“属于”一个特定的 Outer 实例,并且可以将该实例称为 Outer.this。在 Innerx 本身指的是它自己的 属性,所以如果你需要封闭实例的 x 属性,你写 Outer.this.x.