scala de méthode générique prioritaire
-
30-09-2019 - |
Question
J'ai une classe abstraite:
abstract class Foo(...){
def bar1(f : Foo) : Boolean
def bar2(f : Foo) : Foo
}
plusieurs classes étendent Foo et remplacent les méthodes
class FooImpl(...) extends Foo{
override def bar1(f : Foo) : Boolean {
...
}
override def bar2(f : Foo) : Foo {
...
}
}
Est-il possible, en utilisant des génériques (ou quelque chose) pour que les méthodes prépondérants ont le ParameterType de la sous-classe sa mise en œuvre? Comme ceci:
class FooImpl(...) extends Foo{
override def bar1(f : FooImpl) : Boolean {
...
}
override def bar2(f : FooImpl) : FooImpl {
...
}
}
Je pensais quelque chose le long de la ligne de ce qui suit, mais cela ne semble pas fonctionner ...
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{
...
}
}
Toute aide est très appréciée!
Merci.
La solution
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
}
Dans cette version, les différentes sous-classes de Foo
toutes Foo
d'actions comme superclasse, mais pour maintenir la valeur de retour de bar2
(ou les paramètres à bar1
ou bar2
) dans un contexte où tout ce que vous savez sur votre objet (disons qu'il est nommé obj
) est que c'est un Foo
, vous devez utiliser le obj.T
de type que le type de la variable.
Autres conseils
Pour faire deuxième version un peu de peu de Ken Blum plus agréable, vous pouvez utiliser les types d'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
}
besoins de T
pour être un paramètre de type sur la classe Foo
que vous héritez de, non pas sur les méthodes elles-mêmes.
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
}
Les différentes sous-classes de Foo
n'ont pas fait une commune supertype dans cette version du code, car ils vont de différentes paramétrisation de Foo
. Vous pouvez utiliser des méthodes paramétrées qui se réfèrent à Foo[T]
lorsque vous avez besoin de travailler avec le supertype commun, mais je tendance à préférer la solution de type abstrait que j'ai posté dans mon autre réponse, car du coup il ne fuit pas les détails des génériques à tous les d'autres fonctions qui doivent faire face à Foos.
Idéalement, vous combinez les choses mentionnées ci-dessus, i.e..
trait Foo[T <: Foo[T]] { self:T =>
"[T <: Foo [T]]" T est un moyen de sous-classe Foo [T], ET « auto: T => ». Foo signifie que [T] est sous-classe de T, et en même temps, il est un peu étrange façon de dire que Foo [T] est exactement le même que T
Seulement avec que je pouvais faire du code ci-dessous et la compilation fonctionnent comme prévu:
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
}
}
}
Vous pouvez paramétrer Foo
pour accomplir une partie de l'effet facilement:
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...
Si vous voulez exclure le deuxième cas, il n'y a aucun moyen simple de le faire avec les caractéristiques actuelles de Scala.
En outre, certains de ce que vous semblez vouloir ne pas de sens si vous donnez une mise en œuvre effective dans Foo
. Si les promesses de Foo
de prendre toute Foo
mais vous donnent une méthode qui insiste sur seulement Food
, il se brisera si vous vous passez une sous-classe différente de Foo
(par exemple Fool
). Donc, le compilateur ne vous laissera pas faire.
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!