Scala中能否编写接收任意参数并返回泛型实例的泛型函数?
Scala泛型实例化函数的实现方案
可以实现,先说明你原有代码报错的核心原因:
你传入的Set[String]("hello", "test")运行时实际是scala.collection.immutable.HashSet的实例,ct.runtimeClass获取的是这个具体实现类的Class对象,但你的Test类构造函数参数是Set[String](对应Java层面的scala.collection.Set接口),所以getConstructor找不到匹配的构造方法,才会抛出NoSuchMethodException。
下面给出两种可行的实现方式:
方式一:显式指定构造参数类型
直接传入构造函数的参数类型数组,精准匹配目标构造方法:
def to[T](clazz: Class[T], paramTypes: Class[_]*)(args: Any*): T = clazz.getConstructor(paramTypes: _*) .newInstance(args.map(_.asInstanceOf[AnyRef]): _*)
测试调用示例:
class Test(params: Set[String]) // 明确指定构造参数类型为Set的Class对象 to(classOf[Test], classOf[Set[_]])(Set("hello", "test"))
方式二:用TypeTag保留泛型类型信息
如果想保留泛型调用的简洁性,可借助Scala的TypeTag获取参数的精确声明类型,而非运行时实际实例类型:
import scala.reflect.runtime.universe._ def to[T: TypeTag, A: TypeTag](clazz: Class[T], args: A*): T = { val mirror = runtimeMirror(clazz.getClassLoader) val classSymbol = mirror.classSymbol(clazz) val constructor = classSymbol.primaryConstructor.asMethod // 获取泛型参数的声明类型对应的Class val declaredParamClass = typeOf[A].typeSymbol.asClass.runtimeClass // 校验构造函数参数类型匹配 val constructorParam = constructor.paramLists.flatten.head if (constructorParam.info.typeSymbol.asClass.runtimeClass == declaredParamClass) { val instanceMirror = mirror.reflectClass(classSymbol) val constructorMirror = instanceMirror.reflectConstructor(constructor) constructorMirror(args: _*).asInstanceOf[T] } else { throw new NoSuchMethodException(s"No constructor matching parameter type ${declaredParamClass.getName}") } }
测试调用示例:
class Test(params: Set[String]) to[Test, Set[String]](classOf[Test], Set("hello", "test"))
补充说明
- Scala里的
Set、List等集合都是 trait,运行时实例是具体实现类,因此不能用传入实例的Class去匹配构造函数的参数类型; - 第一种方式更轻量高效,适合明确知道构造参数类型的场景;第二种方式依赖Scala反射,能自动识别泛型声明类型,但会有一定性能开销。
内容的提问来源于stack exchange,提问作者sbrk
相关产品推荐
相关产品推荐

