简体   繁体   English

从类[A]获取TypeTag [A]

[英]Get TypeTag[A] from Class[A]

I have createOld method that I need to override and I cannot change it. 我有需要重写的createOld方法,我无法更改它。 I would like to use TypeTag to pattern match provided type in createNew . 我想使用TypeTag模式匹配createNew中提供的类型。 The goal is to find out how to call createNew from createOld . 目的是找出如何从createOld调用createNew My current understanding is that compiler doesn't have enough type information about A in createOld method if it doesn't already come with TypeTag[A] . 我目前的理解是,如果TypeTag[A]尚未createOldcreateOld方法中的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]
  }
}

It is possible to create a TypeTag from a Class using Scala reflection, though I'm not sure if this implementation of TypeCreator is absolutely correct: 可以使用Scala反射从Class创建TypeTag ,尽管我不确定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.")
  })    
}

However, you don't really need full TypeTag if you don't need to inspect generic parameters and full Scala type information. 但是,如果您不需要检查通用参数和完整的Scala类型信息,则实际上并不需要完整的TypeTag You can use ClassTag s for that: 您可以使用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]
}

Or with some implicit sugar: 或加一些隐性糖:

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]
}

You can simplify that even further by using plain old Java newInstance() method: 您可以使用普通的旧Java newInstance()方法进一步简化该操作:

def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]

This, of course, would only work if you don't need different constructor parameters for different classes. 当然,只有在不需要为不同的类使用不同的构造函数参数时,这才起作用。

Calling this createNew from createOld is much simpler than the one with TypeTag s: createOld调用此createNew比使用TypeTag的调用要简单得多:

def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))

So it is not very safe and correct (cause you don't use the power of scala type system), but you can make (using your logic) to do the following: 因此它不是很安全正确(因为您没有使用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

To use it with createOld , just pass implicit argument: 要将其与createOld一起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

I think I should not tell you twice that it is not very good. 我想我不应该再告诉你这不是很好。 We can improve this code by using shapeless : 我们可以通过使用shapeless来改进此代码:

Lets create a poly function, which has a TypeTag as an argument: 让我们创建一个以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!

We also can write a function, in order not to use function getTypeTag[T] every time: 我们还可以编写一个函数,以免每次都不使用函数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!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM