我需要覆盖createOld
方法,我无法更改它。我想在TypeTag
中使用createNew
来模式匹配提供的类型。目标是了解如何从createNew
致电createOld
。我目前的理解是编译器在A
方法中没有足够的关于createOld
的类型信息,如果它还没有TypeTag[A]
附带。
object TypeTagFromClass {
class C1
class C2
// How to get TypeTag[A] needed by createNew?
def createOld[A](c: Class[A]): A = createNew ???
def createNew[A : TypeTag]: A = {
val result = typeOf[A] match {
case a if a =:= typeOf[C1] => new C1()
case a if a =:= typeOf[C2] => new C2()
}
result.asInstanceOf[A]
}
}
答案 0 :(得分:7)
可以使用Scala反射从TypeTag
创建Class
,但我不确定TypeCreator
的此实现是否绝对正确:
import scala.reflect.runtime.universe._
def createOld[A](c: Class[A]): A = createNew {
val mirror = runtimeMirror(c.getClassLoader) // obtain runtime mirror
val sym = mirror.staticClass(c.getName) // obtain class symbol for `c`
val tpe = sym.selfType // obtain type object for `c`
// create a type tag which contains above type object
TypeTag(mirror, new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
}
但是,如果您不需要检查通用参数和完整的Scala类型信息,则实际上并不需要完整TypeTag
。您可以使用ClassTag
:
def createNew[A: ClassTag]: A = {
val result = classTag[A].runtimeClass match {
case a if a.isAssignableFrom(classOf[C1]) => new C1()
case a if a.isAssignableFrom(classOf[C2]) => new C2()
}
result.asInstanceOf[A]
}
或者含有一些隐含的糖:
implicit class ClassTagOps[T](val classTag: ClassTag[T]) extends AnyVal {
def <<:(other: ClassTag[_]) = classTag.runtimeClass.isAssignableFrom(other.runtimeClass)
}
def createNew[A: ClassTag]: A = {
val result = classTag[A] match {
case a if a <<: classTag[C1] => new C1()
case a if a <<: classTag[C2] => new C2()
}
result.asInstanceOf[A]
}
您可以使用普通的旧Java newInstance()
方法进一步简化:
def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
当然,只有在不需要不同类的不同构造函数参数时才能使用。
从createNew
调用此createOld
比使用TypeTag
s的def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))
简单得多:
{{1}}
答案 1 :(得分:0)
所以它不是非常安全和正确(因为你没有使用scala类型系统的强大功能),但你可以(使用你的逻辑)做以下事情:
def createNew[A](implicit t: TypeTag[A]): A = {
val result: Any = t.tpe.toString match {
case "C1" => new C1
case "C2" => new C2
}
result.asInstanceOf[A]
}
createNew[C1] //> its all ok
createNew[C2] //> its all ok
createNew[C3] //> crashes here; lets pretend we got C3 class
要与createOld
一起使用,只需传递隐式参数:
def createOld[A](c: Class[A])(implicit t: TypeTag[A]): A = createNew[A]
createOld[C1] //> its all ok
createOld[C2] //> its all ok
createOld[C3] //> crashes here; lets pretend we got C3 class
我想我不应该告诉你两次它不是很好。
我们可以使用shapeless
:
让我们创建一个poly函数,它有TypeTag
作为参数:
import shapeless._; import scala.reflect.runtime.universe._;
def getTypeTag[T](implicit t: TypeTag[T]) = t //> to get TypeTag of a class
// here is low prority implicit
trait createPolyNewErr extends Poly1 {
implicit def newErr[T] = at[T](_ => "Error can not create object of this class")
}
object createPolyBew extends createPolyNewError {
implicit def newC1 = at[TypeTag[C1]](_ => new C1)
implicit def newC2 = at[TypeTag[C2]](_ => new C2)
}
createPolyNew(getTypeTag[C1]) //> success
createPolyNew(getTypeTag[C2]) //> success
createPolyNew(getTypeTag[C3]) //> String: Error can not create object of this class no crash!
我们也可以编写一个函数,以便每次都不使用函数getTypeTag[T]
:
def createPoly[T]
(implicit t: TypeTag[T],
cse: poly.Case[createPolyNew.type, TypeTag[T] :: HNil]) = cse(t)
createPoly[C1] //> its all ok
createPoly[C2] //> its all ok
createPoly[C3] //> String: Error can not create object of this class no crash!