2011-01-07 9 views
15

Mam abstrakcyjną klasę:scala metoda rodzajowa nadrzędnymi

abstract class Foo(...){ 
    def bar1(f : Foo) : Boolean 
    def bar2(f : Foo) : Foo 
} 

wiele klas przedłużenia Foo i zastąpić Metody

class FooImpl(...) extends Foo{ 
    override def bar1(f : Foo) : Boolean { 
     ... 
    } 
    override def bar2(f : Foo) : Foo { 
     ... 
    } 
} 

to jest możliwe, przy użyciu rodzajowych (lub coś), aby metody nadrzędne mieć typ właściwości podklasy, która go implementuje? Tak:

class FooImpl(...) extends Foo{ 
    override def bar1(f : FooImpl) : Boolean { 
     ... 
    } 
    override def bar2(f : FooImpl) : FooImpl { 
     ... 
    } 
} 

Myślałam coś wzdłuż linii następujących, ale nie wydają się działać ...

abstract class Foo(...){ 
    def bar1[T <: Foo](f : T) : Boolean 
    def bar2[T <: Foo](f : T) : T 
} 

class FooImpl(...) extends Foo{ 
    override def bar1[FooImpl](f : FooImpl) : Boolean { 
     ... 
    } 
    override def bar2[FooImpl](f : FooImpl) : FooImpl{ 
     ... 
    } 
} 

Każda pomoc jest mile widziane!

Dziękuję.

Odpowiedz

17
abstract class Foo{ 
    type T <: Foo 
    def bar1(f:T):Boolean 
    def bar2(f:T):T 
} 

class FooImpl extends Foo{ 
    type T = FooImpl 
    override def bar1(f:FooImpl) = true 
    override def bar2(f:FooImpl) = f 
} 

W tej wersji, różne podklasy Foo wszystko zakładowego Foo jako nadrzędnej, ale do przechowywania wartości zwracanej bar2 (lub parametrów do bar1 lub bar2) w warunkach, w których wszystko wiesz o swoim obiekcie (załóżmy, że jest to nazwa obj) jest to, że jest to Foo, musisz użyć typu obj.T jako typu zmiennej.

+0

pozwoliło to co zamierzałem do zrobienia. Tak więc używając "type T <: Foo", tworzę typ T, który jest albo Foo lub dowolną z jego podklas, i mogę wtedy użyć tego typu w klasie, jak każdy inny typ, gdziekolwiek chcę. Jako parametr, typ zmiennej, wartość zwracana, ... Prawidłowe? Dziękuję Ci bardzo. Na marginesie; Jestem mile zaskoczony szybkością odpowiedzi na to stanowisko. Czapki z głów :-) –

+0

Masz rację. –

2

T musi być parametrem typu na klasie Foo, z którego dziedziczy się, a nie na samych metodach.

abstract class Foo[T <: Foo[T]]{ 
    def bar1(f:T):Boolean 
    def bar2(f:T):T 
} 

class FooImpl extends Foo[FooImpl]{ 
    override def bar1(f:FooImpl) = true 
    override def bar2(f:FooImpl) = f 
} 

Różne podklasy Foo rzeczywistości nie mają wspólny supertypem w tej wersji kodu, ponieważ rozciągają się od różnych parametryzacjami Foo. Możesz użyć sparametryzowanych metod, które odnoszą się do Foo[T], kiedy potrzebujesz pracować z typowym nadtypem, ale preferuję abstrakcyjne rozwiązanie, które zamieściłem w mojej drugiej odpowiedzi, ponieważ nie powoduje to przeciekania szczegółów generycznych do wszystkich inne funkcje, które mają do czynienia z Foos.

0

Można sparametryzować Foo zrealizować niektóre z efektu łatwo:

abstract class Foo[F <: Foo[F]] { def f: F } 
class Food extends Foo[Food] { def f = this } // Yay! 
class Fool extends Foo[Food] { def f = new Food } // Uh-oh... 

Jeśli chcesz wykluczyć drugim przypadku, nie ma bezpośredni sposób to zrobić z obecnych funkcji w Scala.

Ponadto niektóre z tego, co wydaje się być pożądane, nie mają sensu, jeśli podaje się rzeczywistą implementację w Foo. Jeśli Foo obiecuje podjąć dowolną Foo, ale dasz mu metodę, która nalega tylko na Food, zostanie złamana, jeśli podasz mu inną podklasę: Foo (np. Fool). Tak więc kompilator nie pozwoli ci tego zrobić.

abstract class Foo { def bar(f: Foo) : Foo } 
class Foot extends Foo { def bar(f: Foo) = this } // Fine! 
class Fool extends Foo { def bar(f: Fool) = this } // No good! 
+0

Myślę, że możemy zaufać programistce, aby nie definiować "Fool" jako "Foo [Food]", chyba że tak naprawdę zamierza. –

+1

Myślę, że możemy ufać programistom, że nie zamierzają definiować 'Fool' jako' Foo [Food] ', ale błędy się zdarzają. Jeśli błędy się nie zdarzyły, prawdopodobnie nie potrzebowalibyśmy w ogóle sprawdzania typu :) –

12

Aby drugą wersję Kena Blum jest trochę ładniejszy można użyć typów samodzielne:

abstract class Foo[T] { self:T => 
    def bar1(f:T):Boolean 
    def bar2(f:T):T 
} 

class FooImpl extends Foo[FooImpl]{ 
    override def bar1(f:FooImpl) = true 
    override def bar2(f:FooImpl) = f 
} 
1

Idealnie łączą rzeczy wymienione powyżej, to znaczy

trait Foo[T <: Foo[T]] { self:T => 

"[T <: foo [t]]" oznacza T oznacza podklasę Foo [t], I "self: T =>" oznacza, że ​​foo [t] Podklasa T i wspólnie to jest trochę dziwne sposób powiedzieć, że Foo [T] jest dokładnie taka sama jak T.

Tylko że mógłbym zrobić poniższy kod kompilacji i pracować zgodnie z przeznaczeniem:

trait Field[T <: Field[T]] { self:T => 

    def x2:T 

    def +(that:T):T 

    def *(n:BigInt) : T = { 
    if(n == 1) 
     this 
    else if(n == 2) 
     this.x2 
    else if(n == 3) 
     this + this.x2 
    else { 
     val p = (this * (n/2)).x2 
     if (n%2==0) 
     p 
     else 
     p + this 
    }   
    } 

} 
Powiązane problemy