scala generic method overriding
Solution 1
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 this version, different subclasses of Foo
all share Foo
as a superclass, but to hold the return value of bar2
(or the parameters to bar1
or bar2
) in a setting where all you know about your object (let's say it's named obj
) is that it's a Foo
, you need to use the type obj.T
as the type of the variable.
Solution 2
To make Ken Blum's second version a little bit nicer you can use self types:
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
}
Solution 3
T
needs to be a type parameter on the Foo
class that you inherit from, not on the methods themselves.
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
}
Different subclasses of Foo
don't actually have a common supertype in this version of the code, because they extend from different parameterizations of Foo
. You can use parameterized methods that refer to Foo[T]
when you need to work with the common supertype, but I tend to prefer the abstract type solution I posted in my other answer, becuase it doesn't leak the details of the generics to all of the other functions that have to deal with Foos.
Solution 4
Ideally you combine things mentioned above, i.e.
trait Foo[T <: Foo[T]] { self:T =>
"[T <: Foo[T]]" means T is subclass of Foo[T], AND "self:T =>" means that Foo[T] is subclass of T, and together it is a little weird way to tell that Foo[T] is exactly same as T.
Only with that I could make following code compile and work as intended:
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
}
}
}
Jannik Luyten
Updated on July 09, 2022Comments
-
Jannik Luyten almost 2 years
I have an abstract class :
abstract class Foo(...){ def bar1(f : Foo) : Boolean def bar2(f : Foo) : Foo }
multiple classes extend Foo and override the methods
class FooImpl(...) extends Foo{ override def bar1(f : Foo) : Boolean { ... } override def bar2(f : Foo) : Foo { ... } }
Is it possible, using generics (or something) to make the overriding methods have the parametertype of the subclass implementing it? Like this :
class FooImpl(...) extends Foo{ override def bar1(f : FooImpl) : Boolean { ... } override def bar2(f : FooImpl) : FooImpl { ... } }
I was thinking something along the line of the following, but that didn't seem to work...
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{ ... } }
Any help is much appreciated!
Thank you.
-
Ken Bloom over 13 yearsI think we can trust the programmer not to define
Fool
as aFoo[Food]
unless that's what he really intends. -
Rex Kerr over 13 yearsI think we can trust the programmer to not intend to define
Fool
as aFoo[Food]
, but mistakes happen. If mistakes didn't happen, we probably wouldn't need type-checking at all :) -
Jannik Luyten over 13 yearsThis made it possible what I intended to do. So by using "type T <: Foo", I create a type T that is either Foo or any of its subclasses, and I can then use that type within the class like any other type, anywhere I want. Either as a parameter, var type, return value, ... Correct? Thank you very much. On a sidenote; I'm pleasantly surprised on the rapidness of the responses to this post. Hats off :-)
-
Niks almost 4 yearsI laughed hard at this "and together it is a little weird way to tell that Foo[T] is exactly same as T." xD