[EDITED BELOW]
I have a class hierarchy that is, right now, roughly as follows:
sealed trait Foo {
def method: Any
}
case class Bar extends Foo {
def method: Array[String] = // implementation
}
case class Baz extends Foo {
def method: Map[String, Array[String]] = // implementation
}
I am giving the abstract method a return type of Any
because the return types of the case classes are necessarily different, but they share a similar purpose. For this reason, I'd like to keep it in the trait to model that common behavior, and this is the only way I've found to make it compile. I realize this is against the spirit of Scala's type system, so I ask the first question below.
Then, another class expects a subclass of Foo
as a constructor parameter, and I do not know how to denote this, other than the following:
class Qux(foo: Foo) {
val m = foo.method
...
...
}
Later in the class Qux
there are methods that expect the val m
to be of the type corresponding to the particular subclass (Bar
or Baz
) of Foo
, but I am getting compilation errors like
... type mismatch;
[error] found : Any
[error] required: Array[String]
So I have a couple questions:
Qux
that m
should be treated as the value returned by the specific method
from Bar
or Baz
, and not the abstract method from Foo
?Edit: Taking the approach suggested by @marios (using abstract type members) seems to be a step in the right direction, but a type mismatch pops up now. Within the class Qux
, I now have
class Qux[X <: Foo](sc: SparkContext, val foo: X) {
val m: foo.A = foo.method
def process(rows: DataFrame) = foo match {
case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows)
case Baz(sc, _) => BazProcessor(sc, m.keys).doStuff(rows, m.values)
}
}
Where BarProcessor
is instantiated with, for instance, an Array[String]
, and BazProcessor
needs the key-value pairs from the value returned by Baz
's method
to do stuff. However, I am now getting errors like
[error] Qux.scala:4: type mismatch;
[error] found : Qux.this.foo.A
[error] required: Array[String]
[error] case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows)
[error] ^
Similar errors show up when I try to call Map
-specific methods on m
when foo
is a Baz
(along the lines of value keys is not a member of Qux.this.foo.A
, etc.). I understand that m
isn't really an Array[String]
-- it's of type A
. But is there a way to tell Scala to "translate" this into its desired type?
An easier way to access the individual types in your ADT is to use an abstract type member instead of a generic type parameter.
sealed trait Foo {
type A
def method: A
}
case object Bar extends Foo {
type A = Array[String]
def method: A = Array.empty[String]
}
case object Baz extends Foo {
type A = Map[String, Array[String]]
def method: A = Map.empty[String, Array[String]]
}
case class Qux[X <: Foo](foo: X) {
def m: X#A = foo.method
// You can then pattern match on m
def f = m match {
case a: Baz.A => a.size // Use Baz#A if Baz is a class and not an object
case b: Bar.A => b.size // Use Bar#A if Bar is a class and not an object
}
}
Using it (look at the return types)
@ Qux(Baz).m
res6: Map[String, Array[String]] = Map()
@ Qux(Bar).m
res7: Array[String] = Array()
You could add a type parameter to your trait like this:
sealed trait Foo[A] {
def method: A
}
case class Bar extends Foo[Array[String]] {
def method: Array[String]
}
case class Baz extends Foo[Map[String, Array[String]]] {
def method: Map[String, Array[String]]
}
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