Определение количества и типа параметров конструктора с помощью отражения в Scala - PullRequest
2 голосов
/ 23 октября 2019

У меня есть около ста маленьких классов, которые наследуют черту. Классы создаются в фабрике посредством отражения на основе их имен.

Class.forName(name).getConstructor().newInstance().asInstanceOf[Trait]

Теперь требования изменились так, что один и только один из классов должен принимать параметр во время построения. Я пытаюсь изменить фабрику, чтобы справиться с этим новым делом. Рабочий лист REPL с моим подходом выглядит следующим образом:

import java.lang.reflect.Constructor

trait T {
  def p: Unit
}

class C1 extends T {
  override def p = println("no ymd")
}

class C2(a: Array[String]) extends T {
  override def p = println(s"year: ${a(0)}")
}

class C3(a: Array[Int]) extends T {
  override def p = println(s"day: ${a(2)}")
}

val ymd = Array("2019","10","23")
val ymd_int = Array(2019,10,23)

def getT(c: Class[_]): T = {
  c.getConstructors match {
    case Array(c: Constructor[Array[String]]) => c.newInstance(ymd).asInstanceOf[T]
    case Array(c: Constructor[Array[Int]]) => c.newInstance(ymd_int).asInstanceOf[T]
    case Array(c: Constructor[_]) => c.newInstance().asInstanceOf[T]
    case _ => throw new Exception("...")
  }
}

getT(classOf[C1]).p
getT(classOf[C2]).p
getT(classOf[C3]).p 

В REPL я использую classOf вместо Class.forName, потому что имена классов в REPL довольно вялые.

Во время компиляции я получаю предупреждения:

Warning:(25, 23) non-variable type argument Array[String] in type
pattern java.lang.reflect.Constructor[Array[String]] is unchecked 
since it is eliminated by erasure 
case Array(c: Constructor[Array[String]]) =>
    c.newInstance(ymd).asInstanceOf[T]

и

Warning:(26, 23) non-variable type argument Array[Int] in type
pattern java.lang.reflect.Constructor[Array[Int]] is unchecked 
since it is eliminated by erasure
case Array(c: Constructor[Array[Int]]) =>
    c.newInstance(ymd_int).asInstanceOf[T]

И тогда, конечно, вызовы getT терпят неудачу, потому что три оператора case выглядят одинаково навремя выполнения, поэтому все три вызова обрабатываются в первом случае.

Пожалуйста, помогите.

1 Ответ

1 голос
/ 25 октября 2019

Попробуйте

def getT(clazz: Class[_]): T = {
  val constructor = clazz.getConstructors.head
  (constructor.getParameterTypes.headOption.map(_.getSimpleName) match {
    case None             => constructor.newInstance()
    case Some("String[]") => constructor.newInstance(ymd)
    case Some("int[]")    => constructor.newInstance(ymd_int)
    case _                => throw new Exception("...")
  }).asInstanceOf[T]
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...