Почему этот тип не может использоваться для новых экземпляров

Я хотел бы иметь возможность использовать 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

Почему новый класс case не соответствует this.type?

Если я изменяю this.type на Base в Base.new_with_changes и Производится в Derived.new_with_changes, который работает, но кажется, что он пропускает тонкости this.type.

Изменить: реальное намерение вопроса состоит в том, почему у него нет эквивалентного способа в Scala, чтобы объявить, что вызывающий элемент down выполняет downcast, почти так же, как this.type, но для общих типов. Я не думаю, что это будет легко, но было бы хорошо.

Ответы

Ответ 1

[Примечание: я не рекомендую вам это делать.] Есть справедливый шанс, что вы можете выполнить то, что хотите. Приведение в 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

Ответ 2

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 = [email protected]

scala> val o2 = new Outer
o2: Outer = [email protected]


scala> val in1 = new o1.Inner
in1: o1.Inner = [email protected]

scala> val in2 = new o2.Inner
in2: o2.Inner = [email protected]

scala> val o3 = in1.outer
o3: o1.type = [email protected]

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 = [email protected]

scala> b.method1.method2
res3: b.type = [email protected]