我正在尝试在Scala中实现一个方法的协变返回类型。假设我们有以下场景:
class Animal
class Dog extends Animal
class Cat extends Animal
abstract class M[-T]{
def get[U <: T](): U
}
val container = new M[Animal] {
override def get[U <: Animal](): U = ???
}
我该怎么做?
如果你只是好奇,例如,你可以使用Shapeless
import shapeless.{Generic, HNil}
def get[U <: Animal]()(implicit generic: Generic.Aux[U, HNil]): U =
generic.from(HNil)
get[Dog] // Dog() for case class, Dog@34340fab otherwise
get[Cat] // Cat() for case class, Cat@2aafb23c otherwise
get[Nothing] // doesn't compile
get[Null] // doesn't compile
get[Cat with Dog] // doesn't compile
或者你可以使用宏
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def get[U <: Animal](): U = macro getImpl[U]
def getImpl[U: c.WeakTypeTag](c: blackbox.Context)(): c.Tree = {
import c.universe._
q"new ${weakTypeOf[U]}"
}
或者您可以使用运行时反射
import scala.reflect.runtime.universe._
import scala.reflect.runtime
def get[U <: Animal : TypeTag](): U = {
val typ = typeOf[U]
val constructorSymbol = typ.decl(termNames.CONSTRUCTOR).asMethod
val runtimeMirror = runtime.currentMirror
val classSymbol = typ.typeSymbol.asClass
val classMirror = runtimeMirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror().asInstanceOf[U]
}
(另请参见@KrzysztofAtłasik答案中的Java反射示例。(
或者你可以引入一个类型类并手动定义它的实例
trait Get[U <: Animal] {
def get(): U
}
object Get {
implicit val dog: Get[Dog] = () => new Dog
implicit val cat: Get[Cat] = () => new Cat
}
def get[U <: Animal]()(implicit g: Get[U]): U = g.get()
或者,您可以使用反射来创建新实例。你只需要得到隐含的classTag
:
import scala.reflect.ClassTag
class Animal
class Dog extends Animal
class Cat extends Animal
abstract class M[-T] {
def get[U <: T](implicit ct: ClassTag[U]): U
}
val container = new M[Animal] {
override def get[U <: Animal](implicit ct: ClassTag[U]): U =
//it gets no argument constructor so obviosly it will only work if your classes has no params
ct.runtimeClass.getConstructor().newInstance().asInstanceOf[U]
}
container.get[Dog]