Search code examples
scalareflectiontypespattern-matchingscala-reflect

Is there some way in scala that I can return a type?


I have a lot of classes such as DataFrameFlow, TextFlow, RDDFlow. They all derive from base class Flow.

Now I want to write a function judgeFlow which can read from a path: String and return something representing exact Flow type from which I can create corresponding instance. The whole code seems like the following

def judgeFlow(path:String) = /*1*/ {
  Flow.getStoreType(path) match {
    case StoreType.tdw =>
      DataFrameFlow
    case StoreType.hdfs =>
      TextFlow
  }
}

def createFlow(typeInfo:/*2*/) = /*3*/{
  new typeInfo()
}

However, I don't know how to write in place 1, 2 and 3.

EDIT

Knowing how to construct them is not enough here, because I also want the following:

  1. pattern matching through typeInfo
  2. some ways to do asInstanceOf

EDIT 2

Definition of Flow

abstract class Flow(var outputName: String) extends Serializable{
  def this() = this("")
...
}

Definition of DataFrameFlow

class DataFrameFlow(d: DataFrame, path: String) extends Flow {
  var data: DataFrame = d

  def this(data: DataFrame) = this(data, "")
  def this(path: String) = this(null, path)
  def this() = this(null, "")
...
}

Solution

  • Pattern matching can't return different types from different cases. The type returned by pattern matching is the least upper bound of types returned in cases.

    When someone wants to return different types, most probably he/she wants a type class.

    sealed abstract class Flow
    class DataFrameFlow extends Flow
    class TextFlow extends Flow
    class RDDFlow extends Flow
    
    trait JudgeFlow[In] {
      type Out <: Flow
      def judgeFlow(in: In): Out
    }
    object JudgeFlow {
      implicit val `case1`: JudgeFlow[???] { type Out = DataFrameFlow } = ???
      implicit val `case2`: JudgeFlow[???] { type Out = TextFlow } = ???
      implicit val `case3`: JudgeFlow[???] { type Out = RDDFlow } = ???
    }
      
    def judgeFlow[In](in: In)(implicit jf: JudgeFlow[In]): jf.Out = jf.judgeFlow(in)
    

    But the trouble is that types are resolved at compile time. You seem to want to choose a case based on a value of string i.e. at runtime. So you can't return more specific types than just Flow at compile time.

    flatMap with Shapeless yield FlatMapper not found


    It's hard to guess your use case completely.

    But using Scala reflection you can try

    import scala.reflect.runtime.universe._
    import scala.reflect.runtime.currentMirror
    
    def judgeFlow(path:String): Type = {
      Flow.getStoreType(path) match {
        case StoreType.tdw =>
          typeOf[DataFrameFlow]
        case StoreType.hdfs =>
          typeOf[TextFlow]
      }
    }
    
    def createFlow(typeInfo: Type): Flow = {
      val constructorSymbol = typeInfo.decl(termNames.CONSTRUCTOR).asMethod
      val classSymbol = typeInfo.typeSymbol.asClass
      val classMirror = currentMirror.reflectClass(classSymbol)
      val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
      constructorMirror().asInstanceOf[Flow]
    }