Почему этот тип не может использоваться для новых экземпляров
Я хотел бы иметь возможность использовать 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]