问题
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.
回答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.
回答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
}
回答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.
回答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
}
}
}
回答5:
You can parameterize Foo
to accomplish some of the effect easily:
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...
If you want to rule out the second case, there's no straightforward way to do it with the current features in Scala.
Also, some of what you seem to want doesn't make sense if you give an actual implementation in Foo
. If Foo
promises to take any Foo
but you give it a method that insists on only a Food
, it will break if you you pass it a different subclass of Foo
(e.g. Fool
). So the compiler won't let you do that.
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!
来源:https://stackoverflow.com/questions/4626904/scala-generic-method-overriding