I have an abstract Scala class Base
which has subclasses Derived1
and Derived2
. Base
defines a function f() which returns an object of the same type as its implementing class. So Derived1.f()
returns Derived1
and Derived2.f()
returns Derived2
. How do I write this in Scala?
Here is what I have come up with so far.
package com.github.wpm.cancan
abstract class Base {
def f[C <: Base]: C
}
case class Derived1(x: Int) extends Base {
def f[Derived1] = Derived1(x + 1)
}
case class Derived2(x: Int) extends Base {
def f[Derived2] = Derived2(x + 2)
}
This gives the following compiler errors:
type mismatch;
[error] found : com.github.wpm.cancan.Derived1
[error] required: Derived1
[error] def f[Derived1] = Derived1(x + 1)
type mismatch;
[error] found : com.github.wpm.cancan.Derived2
[error] required: Derived2
[error] def f[Derived2] = Derived2(x + 2)
This error message is confusing to me because I think com.github.wpm.cancan.Derived1
should be the same as Derived1
in this context.
Edit: By return type polymorphism I mean overloading the function signature only in the return type. For example, C++ and Java only allow overloading in the type of the formal parameters, not in the return type.
Polymorphism is the ability of any data to be processed in more than one form. The word itself indicates the meaning as means many and. means types. Scala implements polymorphism through virtual functions, overloaded functions and overloaded operators.
=> is syntactic sugar for creating instances of functions. Recall that every function in scala is an instance of a class. For example, the type Int => String , is equivalent to the type Function1[Int,String] i.e. a function that takes an argument of type Int and returns a String .
Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses. The method listOfDuplicates takes a type parameter A and value parameters x and length .
Randall Schulz pointed out one of the reasons your current code doesn't work. It is possible to get what you want, though, with F-bounded polymorphism:
trait Base[C <: Base[C]] { def f: C }
case class Derived1(x: Int) extends Base[Derived1] {
def f: Derived1 = Derived1(x + 1)
}
case class Derived2(x: Int) extends Base[Derived2] {
// Note that you don't have to provide the return type here.
def f = Derived2(x + 2)
}
The type parameter on the base trait allows you to talk about the implementing class there—e.g. in the return type for f
.
Just to add a small precision about (perfectly good) Travis Brown answer: It's not that C
in trait Base[C <: Base[C]]
let's you refer to the implementing class; it's just sticking to the convention of writing subclass extends Base[subclass]
that let's you do so. There's no way that I know of to refer to this type. To clarify what I mean, this compiles
trait Base[C <: Base[C]] { def f: C }
case class Derived1(x: Int) extends Base[Derived1] {
def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
case class Derived2(x: Int) extends Base[Derived1] {
def f = Derived1(x + 2)
}
Now, if all you're going to have as implementations of Base
are case classes, you can get this right through a self-type bound:
trait Base[C <: Base[C]] { self: C =>
def f: C
}
case class Derived1(x: Int) extends Base[Derived1] {
def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
// this won't compile now
case class Derived2(x: Int) extends Base[Derived1] {
def f = Derived1(x + 2)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With