2012-04-24 7 views
5

Mam dwie klasy przypadków, które dziedziczą po abstrakcyjnej klasie bazowej. Chcę zdefiniować niektóre metody na abstrakcyjnej klasie bazowej, które używają metod kopiowania na dziedziczących klasach przypadków (i tak zwróć instancję klasy potomnej). Czy istnieje sposób, aby to zrobić przy użyciu typów własnych?Scala: Czy istnieje sposób, aby klasa nadrzędna miała dostęp do metod zdefiniowanych tylko przez dzieci?

Przykładowy kod:

abstract class BaseClass(a: String, b: Int) { 
    this: case class => //not legal, but I'm looking for something similar 

    def doubleB(newB: Int) = this.copy(b = b * 2) //doesn't work because BaseClass has no copy 
} 

case class HasC(a: String, b: Int, c: Boolean) extends BaseClass(a, b) { 
    def doesStuffWithC(newC: Boolean) = { 
    ... 
    } 
} 

case class HasD(a: String, b: Int, D: Double) extends BaseClass(a, b) { 
    def doesStuffWithD(newD: Double) = { 
    ... 
    } 
} 

Mam zorientowali się, jak uzyskać wynik chcę podziękować tym pytanie: How to use Scala's this typing, abstract types, etc. to implement a Self type? ale polega na dodaniu metodę makeCopy do klasy bazowej i przesłanianie go wywołaniu Kopiuj w każdej z klas przypadków podrzędnych, a składnia (szczególnie w przypadku typu Self) jest dość myląca. Czy istnieje sposób, aby to zrobić z wbudowanym Scala?

+2

Ktoś odpowiedział, że wszystkie klasy sprawy rozszerzają 'Produkt', który jest prawdziwy, ale został usunięty, prawdopodobnie dlatego, że' kopia' nie jest zdefiniowany w 'Produkcie', więc nie działa, aby użyć go dla typu własnego. Dokument "Produkt" mówi "wszystkie klasy przypadków implementują Produkt z syntetycznie generowanymi metodami" - czy to oznacza, że ​​nie mam szczęścia? –

Odpowiedz

5

Nie możesz zrobić, co chcesz, ponieważ copymusi wiedzieć o wszystkich możliwych parametrach. Więc nawet jeśli klasy przypadków odziedziczyły po Copyable, to nie byłby potrzebny copy. Ponadto, jeśli zamierzasz zachować typ prosto, będziesz pokonywany przez brak Scala "MyType". Więc nie możesz tylko przedłużyć klasę podstawową. Jednakże, można dodać metody abstrakcyjne i typ adnotacji:

abstract class BaseClass[C <: BaseClass[_]](a: String, b: Int) { 
    def setB(b0: Int): C 
    def doubleB(b0: Int) = setB(b0*2) 
} 
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass[HasC](a,b) { 
    def setB(b0: Int) = this.copy(b = b0) 
    def doesStuffWithC(c0: Boolean) = doubleB(if (c0) b else -b).copy(c = c0) 
} 

A potem można:

scala> HasC("fish",1,false).doesStuffWithC(true) 
res47: HasC = HasC(fish,2,true) 

Ta dodatkowa praca będzie warto, jeśli masz dużo wspólnego funkcjonalności, które zależą od możliwość kopiowania tylko b (wielu metod lub niewielkiej liczby skomplikowanych metod) - to rozwiązuje problem DRY. Jeśli zamiast tego chcesz zlikwidować ponad HasC i inne pochodne klasy, możesz użyć BaseClass[_] lub dodać kolejny poziom, który definiuje setB(b0: Int): BaseBase lub po prostu zapomnieć o parametryzacji typu i użyć BaseClass jako typu zwracającego (ale rozpoznajesz, że HasC nie może używać metod i nadal posiada metody BaseClass zachować tożsamość typu).

+0

To jest blisko tego, co skończyłem w trakcie implementacji. Egzekwowanie definicji klasy potomnej jako parametru typu w klasie bazowej jest czystsze niż składnia zasugerowana w powyższym pytaniu "Typ własny", więc włączyłem to - dzięki! –

1

Myślę, że nie masz szczęścia. Metody copy na i HasD mają różne sygnatury. Jest nieco ukryty z powodu domyślnych argumentów, ale w zasadzie definicja w BaseClass nie wiedziałaby, którą metodę wywołać.

1

Można zdefiniować makeCopy w klasie abstrakcyjnej, która pobiera funkcję kopiarki, która pobiera Unit i zwraca BaseClass, a następnie, w metodach, które go używają (jak doubleB), nadpisuje je w korpusach klas i wykorzystuje makeCopy przekazując mu anonimową funkcję, która jest dziełem tworzenia nowej kopii z rekwizytami zmienione w taki sposób:

package delegatedcopy 

abstract class BaseClass(a: String, b:Int){ 
    def aField = a 
    def bField = b 
    def doubleB:BaseClass 
    def makeCopy(copier:() => BaseClass):BaseClass = copier() 
} 

case class HasC(override val aField: String, override val bField: Int, cField: Boolean) extends BaseClass(aField, bField){ 
    override def doubleB:BaseClass = makeCopy(()=> HasC(aField, bField * 2, cField)) 
} 

case class HasD(override val aField: String, override val bField: Int, dField:Double) extends BaseClass(aField, bField){ 
    override def doubleB:BaseClass = makeCopy(()=> HasD(aField, bField * 2, dField)) 
} 

app testu, który pokazuje go:

import delegatedcopy._ 

object TestApp extends Application{ 
    val hasC = HasC("A C object", 5, true) 
    val hasD = HasD("A D object", 2, 3.55) 
    val hasCDoubleB = hasC.doubleB 
    val hasDDoubleB = hasD.doubleB 

    println(hasC) // prints HasC(A C object,5,true) 
    println(hasCDoubleB) //prints HasC(A C object,10,true) 

    println(hasD) // prints HasD(A D object,2,3.55) 
    println(hasDDoubleB) // prints HasD(A D object,4,3.55) 
} 

W ten sposób jesteś w stanie utrzymać metodę makeCopy tak samo dla wszystkich dzieci klasy jak w klasie bazowej i może prawdopodobnie implementować lub mieszać całkiem sporo funkcjonalności w klasach bazowych i klasach przypadków, zachowując wspólny kod w bezpiecznym miejscu i będąc w stanie przekazywać klientom klasę BaseClass i wzorce w określonych klasach przypadków.

Powiązane problemy