Scala伴生对象混入Trait能否调用类的构造方法?
Scala 2.12中通过Trait实现默认构造方法的可行方案
原方案不可行的原因
你写的代码无法编译通过,核心原因是Scala不允许通过类型参数T直接调用构造函数new T(code):
- 类型擦除机制导致JVM运行时无法确定
T的具体类型,无法定位对应的构造函数; - 类型参数本身不携带构造函数的元信息,编译器无法验证
T是否存在匹配参数的构造方法。
无论你用Buildable Trait还是BuildableClass抽象类作为约束,new T(...)这种写法在Scala中都是非法的。
可行的实现方式
方式1:Trait定义抽象创建接口,伴生对象实现(推荐,类型安全)
修改Builder Trait,要求混入的伴生对象提供实例创建的具体逻辑,既保留Trait的复用性,又保证类型安全:
trait Builder[T] { // 抽象方法,由伴生对象实现实例创建 protected def createInstance(code: String): T def build(code: String): T = createInstance(code) def build: T = createInstance("bar") } // 可选:用Trait约束可构建的类型 trait Buildable class A(code: String) extends Buildable object A extends Builder[A] { override protected def createInstance(code: String): A = new A(code) }
此时调用A.build("foo")和A.build就能正常创建A的实例,且该Trait可以复用给其他类的伴生对象。
方式2:利用ClassTag+反射(无需重复写创建逻辑,但不推荐)
如果不想每个伴生对象都实现createInstance,可以通过反射结合ClassTag获取构造函数创建实例。但反射存在性能开销,且编译时无法检查构造函数的合法性,运行时可能抛出异常:
import scala.reflect.ClassTag import scala.reflect.runtime.universe._ trait Builder[T <: Buildable : ClassTag : TypeTag] { def build(code: String): T = { val mirror = runtimeMirror(getClass.getClassLoader) val classSymbol = typeOf[T].typeSymbol.asClass val classMirror = mirror.reflectClass(classSymbol) val constructor = typeOf[T].decl(termNames.CONSTRUCTOR).asMethod val constructorMirror = classMirror.reflectConstructor(constructor) constructorMirror(code).asInstanceOf[T] } def build: T = build("bar") } trait Buildable class A(code: String) extends Buildable object A extends Builder[A]
方式3:Scala惯用的apply方法模式
Scala中更常见的实例创建方式是在伴生对象中定义apply方法,虽然需要每个伴生对象写重复代码,但更符合Scala的语言习惯:
trait Buildable class A(code: String) extends Buildable object A { def apply(code: String): A = new A(code) def apply(): A = new A("bar") } // 调用方式:A("foo") 或 A()
总结
你的原始方案无法实现,Scala不支持通过类型参数直接调用构造函数。推荐使用方式1兼顾复用性和类型安全,或直接采用Scala惯用的apply方法模式。
内容的提问来源于stack exchange,提问作者Hektor J.G.
相关产品推荐
相关产品推荐

