создать экземпляр объекта с отражением, используя аргументы конструктора - PullRequest
9 голосов
/ 29 октября 2009

Я пытаюсь выяснить, как создать экземпляр объекта класса case с помощью отражения. Есть ли поддержка для этого? Самое близкое, что я нашел, - это scala.reflect.Invocation, но это больше похоже на выполнение методов, являющихся частью объекта.

case class MyClass(id:Long, name:String)

def instantiate[T](className:String)(args:Any*) : T = { //your code here }

Близок к API, который я ищу.

Любая помощь будет оценена.

Ответы [ 3 ]

19 голосов
/ 29 октября 2009
scala> case class Foo(id:Long, name:String)
defined class Foo

scala> val constructor = classOf[Foo].getConstructors()(0)
constructor: java.lang.reflect.Constructor[_] = public Foo(long,java.lang.String)

scala> val args = Array[AnyRef](new java.lang.Integer(1), "Foobar")
args: Array[AnyRef] = Array(1, Foobar)

scala> val instance = constructor.newInstance(args:_*).asInstanceOf[Foo]
instance: Foo = Foo(1,Foobar)

scala> instance.id
res12: Long = 1

scala> instance.name
res13: String = Foobar

scala> instance.getClass
res14: java.lang.Class[_] = class Foo

В настоящее время в Scala не так много поддержки отражений. Но вы можете вернуться к API Java Reflection. Но есть некоторые препятствия:

  • Вы должны создать Array[AnyRef] и указать свои "примитивные типы" в классах-оболочках (java.lang.Integer, java.lang.Character, java.lang.Double, ...)

  • newInstance(Object ... args) получает массив varargs, равный Object, поэтому вы должны дать подсказке для типа Infiner команду :_*

  • newInstance(...) возвращает Object, поэтому вы должны откинуть его обратно с asInstanceOf[T]

Самое близкое, что я мог бы получить к вашей функции instantiate, это:

def instantiate(clazz: java.lang.Class[_])(args:AnyRef*): AnyRef = {
  val constructor = clazz.getConstructors()(0)
  return constructor.newInstance(args:_*).asInstanceOf[AnyRef]
}

val instance = instantiate(classOf[MyClass])(new java.lang.Integer(42), "foo")
println(instance)           // prints: MyClass(42,foo)
println(instance.getClass)  // prints: class MyClass

Вы не можете получить класс get из универсального типа. Java стирает его (стирание типа).

Редактировать: 20 сентября 2012

Через три года метод instantiate может быть улучшен для возврата правильно напечатанного объекта.

def instantiate[T](clazz: java.lang.Class[T])(args:AnyRef*): T = {
  val constructor = clazz.getConstructors()(0)
  return constructor.newInstance(args:_*).asInstanceOf[T]
}

См. http://www.nabble.com/How-do-I-get-the-class-of-a-Generic--td20873455.html

3 голосов
/ 29 октября 2009
2 голосов
/ 29 октября 2009

Это то, чем я закончил до сих пор, я бы не хотел иметь дело с AnyRef, если это возможно. Так что, если кто-нибудь знает способ обойти это, я буду признателен за помощь.

case class MyClass(id:Long,name:String)

def instantiate[T](classArgs: List[AnyRef])(implicit m : Manifest[T]) : T ={
      val constructor = m.erasure.getConstructors()(0)
      constructor.newInstance(classArgs:_*).asInstanceOf[T]
    }

val list = List[AnyRef](new java.lang.Long(1),"a name")
val result = instantiate[MyClass](list)
println(result.id)
...