Почему this.type нельзя использовать для новых экземпляров - PullRequest
13 голосов
/ 22 апреля 2009

Я хотел бы иметь возможность использовать 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 в Derived.new_with_changes, который работает, но, похоже, ему не хватает тонкости this.type.

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

Ответы [ 2 ]

9 голосов
/ 22 апреля 2009

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
7 голосов
/ 26 апреля 2009

[Примечание: я не рекомендую вам делать это.] Есть большая вероятность, что вы сможете достичь того, чего хотите. Приведение к this.type является ложью, но JVM не знает этого и не может выдать исключение, потому что синглтон-тип является концепцией scala.

Теперь, если вы на самом деле где-то используете свойство singleton этого thistype, это быстро доставит вам неприятности. Но если все, что вы хотите сделать, это получить ковариантные типы возвращаемых данных без необходимости набирать их, только с небольшой обратной стороной огромного уродливого приведения повсеместно:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...