Scala: отражает класс с неявным параметром - PullRequest
1 голос
/ 18 июня 2019

Я пытаюсь отразить класс с неявным параметром, как я могу получить фактический неявный параметр во время выполнения, чтобы получить экземпляр этого класса?

object Validator extends App {

  val between = Between(1, 3)

  println(getAnotherInstance(between))

  def getAnotherInstance[T: TypeTag](obj: T)(implicit tag: ClassTag[T]) = {
    val tpe = typeOf[T]
    val lowerTerm = tpe.member(TermName("lower")).asTerm
    val upperTerm = tpe.member(TermName("upper")).asTerm
    val lower = mirror.reflect(obj).reflectField(lowerTerm).get
    val upper = mirror.reflect(obj).reflectField(upperTerm).get
    val constructorSymbol = typeOf[T].member(termNames.CONSTRUCTOR).alternatives.head.asMethod
    mirror.reflectClass(tpe.typeSymbol.asClass).reflectConstructor(constructorSymbol).apply(lower, upper, Ordering[Int]) // problem is the Ordering[Int]
  }
}

case class Between[T](lower: T, upper: T)(implicit order: Ordering[T]) extends ValidateAnnotation {

  def validate(name: String, value: T): Option[String] = {
    if(order.lteq(value, lower) || order.gteq(value, upper)) Some(s"$name is not between $lower and $upper") else None
  }
}

Фактический тип неявного параметра Ordering [Int] подтверждается во время выполнения, есть ли способ получить неявный параметр (возможно, Ordering [String], Ordering [Date]) во время выполнения? Так что я могу передать его для применения метода, а затем получить экземпляр.

1 Ответ

0 голосов
/ 18 июня 2019

Попробуйте

def getAnotherInstance[T: TypeTag : ClassTag : Ordering](obj: Between[T]) = {
  val tpe = typeOf[Between[T]]
  val lowerTerm = tpe.member(TermName("lower")).asTerm
  val upperTerm = tpe.member(TermName("upper")).asTerm
  val lower = mirror.reflect(obj).reflectField(lowerTerm).get
  val upper = mirror.reflect(obj).reflectField(upperTerm).get
  val constructorSymbol = tpe.member(termNames.CONSTRUCTOR).alternatives.head.asMethod
  mirror.reflectClass(tpe.typeSymbol.asClass).reflectConstructor(constructorSymbol).apply(lower, upper, Ordering[T])
}

getAnotherInstance(Between(1, 3)).asInstanceOf[Between[Int]].validate("aaa", 10) 
// Some(aaa is not between 1 and 3)
getAnotherInstance(Between("a", "c")).asInstanceOf[Between[String]].validate("bbb", "z")
// Some(bbb is not between a and c)

или

def getAnotherInstance[T: TypeTag : ClassTag](obj: T) = {
  val tpe = typeOf[T]
  val lowerTerm = tpe.member(TermName("lower")).asTerm
  val upperTerm = tpe.member(TermName("upper")).asTerm
  val orderTerm = tpe.member(TermName("order")).asTerm
  val lower = mirror.reflect(obj).reflectField(lowerTerm).get
  val upper = mirror.reflect(obj).reflectField(upperTerm).get
  val order = mirror.reflect(obj).reflectField(orderTerm).get
  val constructorSymbol = tpe.member(termNames.CONSTRUCTOR).alternatives.head.asMethod
  mirror.reflectClass(tpe.typeSymbol.asClass).reflectConstructor(constructorSymbol).apply(lower, upper, order)
}

или

def getAnotherInstance[T: TypeTag : ClassTag](obj: T) = {
  val tpe = typeOf[T]
  val lowerTerm = tpe.member(TermName("lower")).asTerm
  val upperTerm = tpe.member(TermName("upper")).asTerm
  val orderTypeArg = tpe.member(TermName("order")).typeSignatureIn(tpe).typeArgs.head
  val lower = mirror.reflect(obj).reflectField(lowerTerm).get
  val upper = mirror.reflect(obj).reflectField(upperTerm).get
  val constructorSymbol = tpe.member(termNames.CONSTRUCTOR).alternatives.head.asMethod

  import scala.tools.reflect.ToolBox
  val order = mirror.mkToolBox().eval(q"Ordering[$orderTypeArg]")

  mirror.reflectClass(tpe.typeSymbol.asClass).reflectConstructor(constructorSymbol).apply(lower, upper, order)
}
...