Scala metodo generico prioritario
-
30-09-2019 - |
Domanda
Ho una classe astratta:
abstract class Foo(...){
def bar1(f : Foo) : Boolean
def bar2(f : Foo) : Foo
}
più classi estendono Foo e sostituiscono i metodi
class FooImpl(...) extends Foo{
override def bar1(f : Foo) : Boolean {
...
}
override def bar2(f : Foo) : Foo {
...
}
}
E 'possibile, utilizzando farmaci generici (o qualcosa del genere) per rendere i metodi imperativi hanno il tipoParametro della sottoclasse sua attuazione? In questo modo:
class FooImpl(...) extends Foo{
override def bar1(f : FooImpl) : Boolean {
...
}
override def bar2(f : FooImpl) : FooImpl {
...
}
}
Stavo pensando qualcosa lungo la linea dei seguenti, ma che non sembra funzionare ...
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{
...
}
}
Ogni aiuto è molto apprezzato!
Grazie.
Soluzione
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
}
In questa versione, diverse sottoclassi di Foo
condividono Foo
come una superclasse, ma per mantenere il valore di ritorno di bar2
(oi parametri per bar1
o bar2
) in un ambiente dove tutto quello che sai del tuo oggetto (diciamo prende il nome obj
) è che si tratta di un Foo
, è necessario utilizzare il tipo di obj.T
come il tipo di variabile.
Altri suggerimenti
Per rendere seconda versione di Ken Blum un po 'più bello è possibile utilizzare i tipi di auto:
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
}
esigenze T
di essere un parametro di tipo sulla classe Foo
che si eredita dalla, non sui metodi stessi.
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
}
Diverse sottoclassi di Foo
non in realtà hanno un supertipo comune in questa versione del codice, perché si estendono da diverse parametrizzazioni di Foo
. È possibile utilizzare i metodi con parametri che fanno riferimento a Foo[T]
quando si ha bisogno di lavorare con il supertipo comune, ma io tendo a preferire la soluzione di tipo astratto ho pubblicato nella mia altra risposta, becuase non perde i dettagli dei farmaci generici a tutte le altre funzioni che hanno a che fare con i Foos.
Idealmente si combinano le cose di cui sopra, cioè.
trait Foo[T <: Foo[T]] { self:T =>
"[T <: Foo [T]]" significa T è sottoclasse di Foo [T], E "auto: T =>". Significa che Foo [T] è sottoclasse di T, ed insieme è un po 'strano modo di dire che Foo [T] è esattamente uguale a T
Solo con che avrei potuto fare seguente codice di compilazione e di lavoro come previsto:
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
}
}
}
È possibile parametrizzare Foo
per realizzare alcuni degli effetti facilmente:
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...
Se si vuole escludere la seconda ipotesi, non c'è modo semplice per farlo con le caratteristiche attuali in Scala.
Inoltre, alcune delle cose che ti sembra di volere non ha senso se si dà un'implementazione reale Foo
. Se promesse Foo
di prendere qualsiasi Foo
ma conferiscono un metodo che insiste solo su un Food
, si romperà se si passa una diversa sottoclasse di Foo
(ad esempio Fool
). Quindi il compilatore non ti consente di farlo.
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!