I have a base class named named
like the following. Its primary constructor takes 4 parameters, flow1
and flow2
are of type T0
and T1
, which are derived from Flow
, an abstract class. The predicate
parameter is a object of FlowParser
, for different EitherFlow
, there can be different FlowParser
.
Now I want to remove in subclasses.
import scala.reflect.ClassTag
import scala.reflect._
abstract class Flow() extends Serializable {
}
trait FlowParser extends Serializable {
def judgeIndex(path: String): Int
def createFlow(path: String): Flow
}
class EitherFlow[T0 >: Null <: Flow: ClassTag, T1 >: Null <: Flow: ClassTag](
predicate: FlowParser,
var flow1: T0,
var flow2: T1,
var path: String) extends Flow{
def this(predi: FlowParser, path: String) = this(predi, null, null, path)
def this(predi: FlowParser) = this(predi, "")
protected def this(predi: FlowParser, index: Int, d: Flow) {
this(predi, null, null, "")
if (index == 0) {
this.flow1 = d.asInstanceOf[T0]
} else {
this.flow2 = d.asInstanceOf[T1]
}
}
def this(predi: FlowParser, d: Flow) {
this(predi, null, null, "")
d match {
case _: T0 => this.flow1 = d.asInstanceOf[T0]
case _: T1 => this.flow2 = d.asInstanceOf[T1]
}
}
}
Now I have many subclasses from EitherFlow
, such as EitherFlow[DataFrameFlow, RawFlow]
. So I may write code like this
new EitherFlow[DataFrameFlow, RawFlow](new MyAnnoyingFlowParser(), "")
However, new MyAnnoyingFlowParser()
is not necessary, because given T0
and T1
are certain, the type of MyAnnoyingFlowParser
is certain, so actually I want this:
new EitherDataFrameOrRawFlow("")
UPDATE:
There are many constructors in EitherFlow
, I want to do the following
// original
new EitherFlow[DataFrameFlow, RawFlow](new MyAnnoyingFlowParser(), a, b, c, ...)
// what I want
new EitherDataFrameOrRawFlow(a, b, c, ...)
I have tried the following strategies, and I meet some problems in both of them.
Try "currying" the
apply
method With this strategy, I want I can write thisval f1 = EitherDataFrameOrRawFlow("") val f2 = EitherDataFrameOrRawFlow(new RawFlow(), "")
So I decide to write the following code, it actually returns a anonymous Object, which has overloaded
apply
method. I adopt this strategy from this post(Scala, currying and overloading)object EitherFlow { def apply[T0 >: Null <: Flow: ClassTag, T1 >: Null <: Flow: ClassTag](predicate: FlowParser) = new { def apply[R >: Null <: Flow: ClassTag](flow: R, path: String): EitherFlow[T0, T1] = { val ret = new EitherFlow[T0, T1](predicate, flow) ret.path = path ret } def apply(path: String): EitherFlow[T0, T1] = { new EitherFlow[T0, T1](predicate, null.asInstanceOf[T0], null.asInstanceOf[T1], path) } } }
However, with this strategy, I have to use
.apply
explicitly. Or, it will cause an errorcom.xxx.flow.EitherDataFrameOrRawFlow.type does not take parameters
Try create a new class and implement its own aux constructors
However, I am confused when I try to implememt aux constructor
def this(d: Flow)
. According to alexander, I can't directly call super's aux constructor. So I have to write like the followingclass EitherDataFrameOrRawFlow(var f1: DataFrameFlow, var f2: RawFlow, path: String) extends EitherFlow[DataFrameFlow, RawFlow](new DataFrameRawFlowParser(), f1, f2, path) { def this(d: Flow) { this(null, null, "") d match { case _: DataFrameFlow => this.flow1 = d.asInstanceOf[DataFrameFlow] case _: RawFlow => this.flow2 = d.asInstanceOf[RawFlow] } } }
However, I think the code is not neat. Firstly, I have to write duplicate code such as
d match {...}
. Second, I have to define duplicate parameters suchasf1
/f2
.