2016-02-06 2 views
0

У меня есть следующий фрагмент кода:Scala: незаконное наследование; selftype Foo [T] не соответствует Foo [T] s selftype T

abstract class Foo[T <: Foo[T]] { self: T => 
    def bar(x: T): T 
    def newFoo: Foo[T] = { 
    new Foo[T] { self: T => 
     // ... 
    } 
} 

}

У меня есть необходимость создания нового экземпляра Foo в методе моего абстрактного класса. Может ли кто-нибудь посоветовать мне, как лучше подойти к этому?

Спасибо, Адил

ответ

0

Самостоятельно типа self: T => означает, что ваш Foo[T] также должен быть T. new Foo[T] { ... } не является экземпляром T для любых произвольных T, что составляет Foo[T]. Вы также не можете добавить self-type в анонимный класс, например new Foo[T] { ... }, потому что это не имеет смысла. Либо конкретный класс является или не является T в этом пункте.

Построить метод типа def newFoo: Foo[T] безопасным типом на самом деле невозможно с самонастройкой на месте, поскольку вам нужно знать, как построить произвольное T. Возможно, вы сможете делать то, что хотите, с отражением, когда каждый T имеет тот же самый конструктор.

import scala.reflect._ 

abstract class Foo[T <: Foo[T] : ClassTag] { self: T => 
    def bar(x: T): T 
    def newFoo: Foo[T] = classTag[T].runtimeClass.newInstance.asInstanceOf[T] 
} 

class Bar extends Foo[Bar] { 
    def bar(x: Bar): Bar = x 
} 

scala> val b = new Bar 
b: Bar = [email protected] 

scala> b.newFoo 
res1: Foo[Bar] = [email protected] 

Это перестает работать, когда есть параметры конструктора:

case class Baz(i: Int) extends Foo[Baz] { 
    def bar(x: Baz): Baz = x 
} 

scala> val baz = new Baz(0) 
baz: Baz = Baz(0) 

scala> baz.newFoo 
java.lang.InstantiationException: Baz 
    at java.lang.Class.newInstance(Class.java:427) 
    at Foo.newFoo(<console>:16) 
+0

Ну, вы уже знаете класс T, даже без каких-либо меток классов, и это происходит именно из-за self-type: self имеет тип T, поэтому с self.getClass вы получаете класс, который хотите. – dth

+0

Правда, но это не поможет вам, если у него есть параметры конструктора. –

+0

Нет, действительно. Вы можете, конечно, также получить другие конструкторы таким образом, но тогда остается вопрос, откуда вы получите аргументы. – dth

0

Ну, вы не знаете конкретный класс, где Foo будет наследоваться в будущем. Поэтому, особенно, вы не знаете, какие параметры конструктора у этого класса будут иметь и как предоставить аргументы для них. Если вы действительно захотите это сделать, вам нужно будет сделать некоторые предположения (которые вы не можете обеспечить во время компиляции) для достижения этого.

Так что вы, вероятно, должны сделать, это оставить этот метод абстрактным и реализовать его в подклассе. Если это не вариант, у вас, вероятно, есть некоторые проблемы с вашим общим дизайном классов. Здесь лучше представить, что вы хотите смоделировать и попросить о помощи.

Если предположить, что конструктор конкретного класса не будет иметь какие-либо параметров, вы можете реализовать newFoo как

def newFoo = this.getClass.newInstance 

Там нет необходимости для classTags или других модных вещей.