scala 在集合中存储不同类型的最佳方式是什么

ulmd4ohb  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(162)

我有一个泛型类:

class GenericType[T] {
  def func(t: T) = ???
}

我需要实现一个函数,该函数接受List[String]并输出相应的GenericType[T]。例如,如果客户端传入List("SomeType1", "SomeType2"),则函数应返回List(GenericType[SomeType1], GenericType[SomeType2])。基本上,有一个Map到类型的字符串。
我找不到一种很好的方法来表示这种函数的返回类型。可以选择Seq[GenericType[_]]作为返回类型,但它要求客户端将其强制转换为相应的子类,以便在类型信息丢失时调用func
或者,也可以使用Case类,但这并不灵活,因为每次添加新的子类时都需要修改Case类。

case class (s1: Option[GenericType[SomeType1]] = None, s2: Option[SomeType2] = None, ...)

我很好奇表示返回类型的好方法是什么?

mspsb9vt

mspsb9vt1#

最简单的是

def stringToGenericType(s: String): GenericType[_] = s match {
  case "SomeType1" => new GenericType[SomeType1]
  case "SomeType2" => new GenericType[SomeType2]
}

GenericType[_](如果将GenericType协变为class GenericType[+T],则为GenericType[Any])将是诚实的返回类型。您不能在编译时根据运行时字符串知道返回哪个特定的GenericType[...]。无论如何,你不能避免在某个地方进行类型转换,因为你不能保证类型是静态的。无论如何,这种编程不可能是类型安全的。
因为我们是基于运行时字符串进行选择,所以编译时技术(宏、隐式、形式化)不在考虑之列。(*)
实际上,目前您甚至不需要运行时反射。根据T类型的不同,您的类GenericType[T]和其中的func方法在运行时似乎没有做任何不同的事情。GenericType[SomeType1]GenericType[SomeType2]在运行时只是GenericType[_]。因此,即使是以下实现也是可能的

def stringToGenericType(s: String): GenericType[_] = new GenericType[Any]

另一种情况是,如果创建了不同类的示例

class GenericType1[T]
class GenericType2[T]

import scala.reflect.runtime
import scala.reflect.runtime.universe._
val runtimeMirror = runtime.currentMirror

def stringToGenericTypeX(s: String): Any = {
  val classSymbol = runtimeMirror.staticClass(s)
  val constructorSymbol = classSymbol.typeSignature.decl(termNames.CONSTRUCTOR).asMethod
  runtimeMirror.reflectClass(classSymbol).reflectConstructor(constructorSymbol).apply()
}

或者调用了不同的方法

class GenericType[T] {
  def func1(t: T) = ???
  def func2(t: T) = ???
}

def callFuncX(methodName: String, t: Any) = {
  val classSymbol = runtimeMirror.classSymbol(classOf[GenericType[_]])
  val methodSymbol = classSymbol.typeSignature.decl(TermName(methodName)).asMethod
  runtimeMirror.reflect(new GenericType[Any]).reflectMethod(methodSymbol).apply(t)
}

或者根据T类型的不同,在运行时的行为有所不同

class GenericType[T: ClassTag] {
  def func(t: T) = println(classTag[T].runtimeClass)
}

import scala.tools.reflect.ToolBox
val toolbox = runtimeMirror.mkToolBox()

def stringToGenericType(s: String): GenericType[_] = {
  toolbox.eval(q"new GenericType[${TypeName(s)}]").asInstanceOf[GenericType[_]]
}

(*)好的,实际上第一个模式匹配(正如我所写的)可以用宏自动完成

// in a different subproject
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def stringToGenericType(s: String): GenericType[_] = macro stringToGenericTypeImpl

def stringToGenericTypeImpl(c: blackbox.Context)(s: c.Tree): c.Tree = {
  import c.universe._
  val cases = List("SomeType1", "SomeType2").map(str =>
    cq"$str => new GenericType[${TypeName(str)}]"
  )
  q"$s match { case ..$cases }"
}
val s = "SomeTime1"
stringToGenericType(s)

     //  scalacOptions += "-Ymacro-debug-lite"
//s match {
//  case "SomeType1" => new GenericType[SomeType1]()
//  case "SomeType2" => new GenericType[SomeType2]()
//}

相关问题