【问题标题】:What is the use of `*.this` in Scala?Scala 中的 `*.this` 有什么用?
【发布时间】:2021-09-10 19:22:26
【问题描述】:
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

【问题讨论】:

  • 就像在 Java 中一样,this 在没有歧义的情况下是完全可选的。 R 更不常见,因为只有在您需要区分要访问的父类时才需要它。
  • @LuisMiguelMejíaSuárez 然后你会在答案中放一些例子,清楚地表明它在哪里模棱两可或更清楚地使用它?
  • 对不起,我在用手机,写代码是一场噩梦。经典的例子是手工制作的二传手,非常非常奇怪。当我收到同一个类的另一个实例时,有时我喜欢使用this,而根本不需要我喜欢让它更详细和清晰。 - 希望有人稍后会发布完整的答案。

标签: scala path-dependent-type


【解决方案1】:

一个规则是 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$1@6d3ad37a

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

注意res1 的返回类型是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$1@7d381eae

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

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

注意在我们显式声明单例返回类型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$1@6a9a6a0c

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

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

注意f() 调用是如何输入到Foo 的,而Bar 可能不是预期的。另一方面,如果我们提供显式的单例返回类型this.type 那么

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$1@4707d60a

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

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

我们看到f() 呼叫键入到Bar

这些是机制,但实际应用呢?我知道的两种用途是:


【讨论】:

    【解决方案2】:

    这对内部类很重要。例如

    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.thisInner 中的 x 本身是指它自己的属性,因此如果您需要封闭实例的 x 属性,请编写 Outer.this.x

    【讨论】:

      猜你喜欢
      • 2021-02-21
      • 2022-01-23
      • 2011-04-10
      • 2010-11-06
      • 2011-04-12
      • 2013-04-30
      • 2013-12-20
      • 1970-01-01
      相关资源
      最近更新 更多