Reputation: 736
This is a follow up question on my previous question: Scala - Passing Class of derived type in place of Class of supertype
Following is my use case:
I have a class called MultiplePredictionTester
with the following implementation...
class MultiplePredictionTester[T <: SurvivalPredictor](predictorClasses: Seq[Class[T]],
restOfConstructorParams: Map[Class[T], Seq[Object]],
dataSplitter: DataSplitter,
titanic: DataFrame) extends PredictionTester {
import titanic.sqlContext.implicits._
override def test: Map[SurvivalPredictor, Double] = ???
}
The idea is to be able to take a sequence of classes derived from SurvivalPredictor
and instantiate them inside, using a constructor where first argument is fetched from DataSplitter
while the rest of the arguments come from the Map[Class[T], Seq[Object]]
I plan to invoke this class as:
object MultiQuickRunner extends App with ResourceGetter {
val conf: SparkConf = new SparkConf().setAppName("TitanicSurvivalPredictor").setMaster("local[4]")
val sc: SparkContext = new SparkContext(conf)
val sqlContext: SQLContext = new SQLContext(sc)
val test: Map[SurvivalPredictor, Double] =
new MultiplePredictionTester(Seq(classOf[SexBasedPredictor], classOf[TotallyRandomPredictor]),
Map[Class[SurvivalPredictor],Seq[Object]](),
new DataSplitter {},
new DataFrameLoader {}.load(getPathForResource("train.csv"),
sqlContext)).test
test.foreach { case (survivalPredictorInstance, accuracy) =>
println("Accuracy for " + survivalPredictorInstance.getClass.getName + " is " + accuracy)
}
}
However trying to compile something like that throws me the exception:
Error:(27, 5) no type parameters for constructor MultiplePredictionTester: (predictorClasses: Seq[Class[T]], restOfConstructorParams: Map[Class[T],Seq[Object]], dataSplitter: com.dhruvk.kaggle.DataSplitter, titanic: org.apache.spark.sql.DataFrame)com.dhruvk.kaggle.predictiontesters.implementations.MultiplePredictionTester[T] exist so that it can be applied to arguments (Seq[Class[_ >: com.dhruvk.kaggle.predictors.implementations.TotallyRandomPredictor with com.dhruvk.kaggle.predictors.implementations.SexBasedPredictor <: com.dhruvk.kaggle.predictors.SurvivalPredictor]], scala.collection.immutable.Map[Class[com.dhruvk.kaggle.predictors.SurvivalPredictor],Seq[Object]], com.dhruvk.kaggle.DataSplitter, org.apache.spark.sql.DataFrame)
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Seq[Class[_ >: com.dhruvk.kaggle.predictors.implementations.TotallyRandomPredictor with com.dhruvk.kaggle.predictors.implementations.SexBasedPredictor <: com.dhruvk.kaggle.predictors.SurvivalPredictor]]
required: Seq[Class[?T]]
new MultiplePredictionTester(Seq(classOf[SexBasedPredictor], classOf[TotallyRandomPredictor]),
^
Any help is appreciated.
Upvotes: 0
Views: 511
Reputation: 170735
As my comment to Till Rohrman's answer to the linked question mentions, there is just one Class[T]
for any specific class or trait T
: classOf[T]
. So your predictorClasses
can have length at most 1, and restOfConstructorParams
can have size of most one. This is not what you want. The correct signature would use existential types:
class MultiplePredictionTester(predictorClasses: Seq[Class[_ <: SurvivalPredictor]],
...
Class[_ <: SurvivalPredictor]
is short for Class[T <: SurvivalPredictor] forSome { type T }
and is the common type for all Class
objects of subtypes of SurvivalPredictor
.
It isn't clear what you want to use restOfConstructorParams
for, but the type you gave is also wrong. It might be Seq[Any]
, for example.
Upvotes: 1
Reputation: 3887
java.lang.Class
is invariant. During class declaration you are telling compiler that it is some T which has a upper bound such that T <: SurvivalPredictor
. But when initializing you are saying T could SexBasedPredictor, TotallyRandomPredictor or SurvivalPredictor
. This is not possible.
Consider
class A
class B extends A
class Test[T <: A](x: Seq[Class[T]], m: Map[Class[T], Seq[T]])
Here I have a class Test with type parameter T with upper bound A. Now I can initialize Test with
new Test(Seq(classOf[A], classOf[A]), Map(classOf[A] -> Seq(new B)))
As java.lang.Class
is invariant, once I state T
as A
. I cant change it. If I change to B
, compiler will yell at me. On the other hand as Seq
is covariant I can say new B
there.
Upvotes: 1