2011-02-02 6 views
5

Estoy intentando invocar el método newInstance de un constructor de una clase Scala (clase de caso o clase habitual, ambos se ven afectados).Número incorrecto de argumentos que invocan un constructor de Scala utilizando el reflejo

Sin embargo, estoy corriendo en un IllegalArgumentException con la sugerencia número incorrecto de argumentos.

considerar lo siguiente:

case class Vec2(x: Float, y: Float) 

object TestApp { 
    def main(args: Array[String]) { 
    //after some research I found the last constructor always to be the default 
    val ctor = classOf[Vec2].getConstructors.last 

    println("ctor = " + ctor) 
    println("takes parameters: " + ctor.getParameterTypes.length) 

    val params = new Array[Float](2) 

    params.update(0, 1.0f) 
    params.update(1, -1.0f) 

    println("num parameters: " + params.length) 

    println("trying to create new instance...") 
    try { 
     val x = ctor.newInstance(params) 
     println("new instance: " + x) 
    } 
    catch { 
     case ex => ex.printStackTrace 
    } 
    } 

La salida es la siguiente:

ctor = public pd.test.Vec2(float,float) 
takes parameters: 2 
num parameters: 2 
trying to create new instance... 
java.lang.IllegalArgumentException: wrong number of arguments 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513) 
    at pd.test.TestApp$.main(TestApp.scala:60) 
    at pd.test.TestApp.main(TestApp.scala) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115) 

he experimentado algo como esto en Java una vez. En ese caso, la clase que intentaba crear una instancia era una clase interna de otra clase, por lo que Java esperaba un parámetro adicional implícito, que era el objeto Class de la clase adjunta (si la clase se declaró como estática) o una instancia de la clase adjunta

Sin embargo, en este caso no hay ninguna clase envolvente de Vec2, a menos Scala agrega uno internamente (aunque, java.lang.Class.getEnclosingClass() vuelve null para mí).

Así que mi pregunta es cómo instanciar las clases de Scala utilizando la reflexión? ¿Hay algún parámetro adicional que los constructores de Scala esperan implícitamente?

Respuesta

11

El método newInstance toma un parámetro varargs. En Scala (a diferencia de Java), no puede pasar una matriz y tratarla automáticamente como todos los argumentos. Si eso es lo que quieres que tiene que hacer de forma explícita como esto:

ctor.newInstance(params:_*) 

Lo que está haciendo en este momento está pasando un array con 2 elementos como el primer argumento del constructor.

+1

'newInstance' excepto los argumentos del subtipo' java.lang.Object', por lo que tendrá que hacer algo como 'ctor.newInstance (params.map (_. AsInstanceOf [Object]): _ *)' for ' Float' –

+0

¡Gracias, eso ayudó! También gracias Vasil. No me encontré con ese problema con la clase genérica en la que estoy trabajando, que crea una matriz [AnyRef]. El ejemplo que publiqué fue una versión simplificada. :) – pdinklag

+0

@Vasil Al igual que con Java, Scala autoboxará cualquier cosa que tenga un tipo primitivo subyacente, por lo que un Scala 'Float' puede ser un' float' o un 'java.lang.Float'. Debido a que scala varargs usa 'Seq' y Seq es un tipo borrado, puede estar seguro de que lo que está pasando ya estará encasillado y subclasificará' Object' –

Cuestiones relacionadas