java: ¿cómo puedo hacer fundición dinámica de una variable de un tipo a otro?

Me gustaría hacer fundición dinámica para una variable de Java, el tipo de fundición se almacena en una variable diferente.

esto es casting regular:

String a = (String) 5; 

esto es lo que quiero:

  String theType = 'String'; String a = (theType) 5; 

¿Es posible? y si es así, ¿cómo? ¡Gracias!

actualizar

Estoy intentando completar una clase con un hashMap que recibí.

este es el constructor:

 public ConnectParams(HashMap obj) { for (Map.Entry entry : obj.entrySet()) { try { Field f = this.getClass().getField(entry.getKey()); f.set(this, entry.getValue()); /* <= CASTING PROBLEM */ } catch (NoSuchFieldException ex) { log.error("did not find field '" + entry.getKey() + '"'); } catch (IllegalAccessException ex) { log.error(ex.getMessage()); } } } 

el problema aquí es que algunas de las variables de clase son de tipo Doble, y si se recibe el número 3, lo ve como Entero y tengo un problema de tipo.

En cuanto a su actualización, la única forma de resolver esto en Java es escribir código que cubra todos los casos con muchas expresiones if y else e instanceof . Lo que intenta hacer se ve como si se utilizara para progtwigr con lenguajes dynamics. En los lenguajes estáticos, lo que intenta hacer es casi imposible y es probable que uno elija un enfoque totalmente diferente para lo que intenta hacer. Los lenguajes estáticos simplemente no son tan flexibles como los dynamics 🙂

Buenos ejemplos de las mejores prácticas de Java son la respuesta de BalusC (es decir, ObjectConverter ) y la respuesta de Andreas_D (es decir, Adapter ) a continuación.


Eso no tiene sentido, en

 String a = (theType) 5; 

el tipo de a está estáticamente ligado a String por lo que no tiene ningún sentido tener un molde dynamic para este tipo estático.

PD: la primera línea de su ejemplo podría escribirse como Class stringClass = String.class; pero aún así, no puedes usar stringClass para moldear variables.

Sí, es posible usando Reflection

 Object something = "something"; String theType = "java.lang.String"; Class theClass = Class.forName(theType); Object obj = theClass.cast(something); 

pero eso no tiene mucho sentido ya que el objeto resultante debe guardarse en una variable de tipo Objeto. Si necesita que la variable sea de una clase determinada, puede lanzar a esa clase.

Si desea obtener una clase determinada, Number por ejemplo:

 Object something = new Integer(123); String theType = "java.lang.Number"; Class theClass = Class.forName(theType).asSubclass(Number.class); Number obj = theClass.cast(something); 

pero todavía no tiene sentido hacerlo así, podrías simplemente lanzar a Número.

El lanzamiento de un objeto NO cambia nada; es solo la forma en que el comstackdor lo trata.
La única razón para hacer algo así es verificar si el objeto es una instancia de la clase dada o de cualquier subclase de la misma, pero sería mejor hacerlo usando instanceof o Class.isInstance() .

Actualizar

según su última actualización, el problema real es que tiene un entero en su HashMap que debería asignarse a un doble. Lo que puede hacer en este caso es verificar el tipo de campo y usar los métodos xxxValue() de Number

 ... Field f = this.getClass().getField(entry.getKey()); Object value = entry.getValue(); if (Integer.class.isAssignableFrom(f.getType())) { value = Integer.valueOf(((Number) entry.getValue()).intValue()); } else if (Double.class.isAssignableFrom(f.getType())) { value = Double.valueOf(((Number) entry.getValue()).doubleValue()); } // other cases as needed (Long, Float, ...) f.set(this, value); ... 

(no estoy seguro si me gusta la idea de tener el tipo incorrecto en el Mapa)

Tendrá que escribir una especie de ObjectConverter para esto. Esto es factible si tiene el objeto que desea convertir y conoce la clase de destino a la que desea convertir. En este caso particular, puede obtener la clase objective por Field#getDeclaringClass() .

Puede encontrar aquí un ejemplo de dicho ObjectConverter . Debería darte la idea base. Si desea más posibilidades de conversión, solo agregue más métodos con el argumento deseado y el tipo de devolución.

Puede hacerlo utilizando el método Class.cast() , que dinámicamente arroja el parámetro proporcionado al tipo de instancia de clase que tiene. Para obtener la instancia de clase de un campo en particular, use el método getType() en el campo en cuestión. He dado un ejemplo a continuación, pero tenga en cuenta que omite todo el manejo de errores y no debe utilizarse sin modificaciones.

 public class Test { public String var1; public Integer var2; } public class Main { public static void main(String[] args) throws Exception { Map map = new HashMap(); map.put("var1", "test"); map.put("var2", 1); Test t = new Test(); for (Map.Entry entry : map.entrySet()) { Field f = Test.class.getField(entry.getKey()); f.set(t, f.getType().cast(entry.getValue())); } System.out.println(t.var1); System.out.println(t.var2); } } 

Funciona e incluso hay un patrón común para su enfoque: el patrón del Adaptador . Pero, por supuesto, (1) no funciona para enviar primitivas Java a objetos y (2) la clase tiene que ser adaptable (por lo general, implementando una interfaz personalizada).

Con este patrón, podrías hacer algo como:

 Wolf bigBadWolf = new Wolf(); Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class); 

y el método getAdapter en la clase Wolf:

 public Object getAdapter(Class clazz) { if (clazz.equals(Sheep.class)) { // return a Sheep implementation return getWolfDressedAsSheep(this); } if (clazz.equals(String.class)) { // return a String return this.getName(); } return null; // not adaptable } 

Para ti, idea especial, eso es imposible. No puede usar un valor String para lanzar.

Puedes escribir un método de cast simple como el de abajo.

 private  T castObject(Class clazz, Object object) { return (T) object; } 

En tu método deberías usarlo como

 public ConnectParams(HashMap object) { for (Map.Entry entry : object.entrySet()) { try { Field f = this.getClass().getField(entry.getKey()); f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */ } catch (NoSuchFieldException ex) { log.error("did not find field '" + entry.getKey() + '"'); } catch (IllegalAccessException ex) { log.error(ex.getMessage()); } } } 

Tu problema no es la falta de “casting dynamic”. Casting Integer to Double no es posible en absoluto. Parece que quiere darle a Java un objeto de un tipo, un campo de un tipo posiblemente incompatible, y hacer que de alguna manera descubra automáticamente cómo convertir entre los tipos.

Este tipo de cosas es anatema para un lenguaje fuertemente tipado como Java e IMO por muy buenas razones.

¿Qué estás tratando de hacer? Todo ese uso de la reflexión parece bastante sospechoso.

No hagas esto Solo tiene un constructor parametrizado correctamente. El conjunto y los tipos de los parámetros de conexión se fijan de todos modos, por lo que no tiene sentido hacer esto de forma dinámica.

Por lo que vale la pena, la mayoría de los lenguajes de scripting (como Perl) y los lenguajes de tiempo de comstackción no estáticos (como Pick) son compatibles con las conversiones de objetos de cadenas dinámicas en tiempo de ejecución (relativamente arbitrarias). Esto también se puede lograr en Java sin perder la seguridad de tipo y los buenos lenguajes de tipo estático brindan SIN los desagradables efectos secundarios de algunos de los otros lenguajes que hacen cosas malvadas con el casting dynamic. Un ejemplo de Perl que hace algunas matemáticas cuestionables:

 print ++($foo = '99'); # prints '100' print ++($foo = 'a0'); # prints 'a1' 

En Java, esto se logra mejor (en mi humilde opinión) mediante el uso de un método que llamo “cross-casting”. Con cross-casting, la reflexión se usa en un caché de constructores y métodos perezosos que se descubren dinámicamente a través del siguiente método estático:

 Object fromString (String value, Class targetClass) 

Desafortunadamente, ningún método incorporado de Java como Class.cast () hará esto para String a BigDecimal o String a Integer o cualquier otra conversión donde no haya una jerarquía de clases de soporte. Por mi parte, el punto es proporcionar una forma totalmente dinámica para lograr esto, para lo cual no creo que la referencia previa sea el enfoque correcto, teniendo que codificar cada conversión. En pocas palabras, la implementación es solo para lanzar desde cadena si es legal / posible.

Entonces, la solución es una simple reflexión en busca de miembros públicos de:

STRING_CLASS_ARRAY = (new Class [] {String.class});

a) Miembro miembro = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Miembro miembro = targetClass.getConstructor (STRING_CLASS_ARRAY);

Encontrará que todas las primitivas (Integer, Long, etc.) y todos los conceptos básicos (BigInteger, BigDecimal, etc.) e incluso java.regex.Pattern están cubiertos a través de este enfoque. Lo he usado con éxito significativo en proyectos de producción donde hay una gran cantidad de entradas arbitrarias de valor de cadena donde se necesitaba una comprobación más estricta. En este enfoque, si no hay un método o cuando se invoca el método, se lanza una excepción (porque es un valor ilegal, como una entrada no numérica a un BigDecimal o RegEx ilegal para un patrón), que proporciona la comprobación específica para la lógica inherente de la clase objective.

Hay algunas desventajas en esto:

1) Debes entender bien la reflexión (esto es un poco complicado y no para principiantes). 2) Algunas de las clases de Java y de hecho las bibliotecas de terceros están (sorpresa) no codificadas correctamente. Es decir, hay métodos que toman un argumento de cadena única como entrada y devuelven una instancia de la clase objective, pero no es lo que piensas … Considera la clase Entero:

 static Integer getInteger(String nm) Determines the integer value of the system property with the specified name. 

El método anterior realmente no tiene nada que ver con enteros como objetos que envuelven primitivas. Reflection lo encontrará como un posible candidato para crear un Entero a partir de una Cadena incorrectamente en comparación con los Miembros de decodificador, valor y constructor, que son adecuados para la mayoría de las conversiones de Cadenas arbitrarias en las que realmente no tiene control sobre sus datos de entrada pero solo desea saber si es posible un Entero.

Para remediar lo anterior, buscar métodos que arrojen Excepciones es un buen comienzo porque los valores de entrada no válidos que crean instancias de dichos objetos deberían arrojar una excepción. Lamentablemente, las implementaciones varían en cuanto a si las excepciones se declaran como comprobadas o no. Integer.valueOf (String) arroja una NumberFormatException marcada por ejemplo, pero las excepciones Pattern.compile () no se encuentran durante las búsquedas de reflexión. Nuevamente, no es un fracaso de este enfoque dynamic de “cross-casting”, sino una implementación muy poco estándar para las declaraciones de excepciones en los métodos de creación de objetos.

Si alguien desea obtener más detalles sobre cómo se implementó lo anterior, hágamelo saber, pero creo que esta solución es mucho más flexible / extensible y con menos código sin perder las buenas partes de la seguridad de tipo. Por supuesto, siempre es mejor “conocer tus datos”, pero como muchos de nosotros encontramos, a veces solo somos destinatarios de contenido no administrado y tenemos que hacer todo lo posible para usarlo correctamente.

Aclamaciones.

Entonces, esta es una publicación anterior, sin embargo, creo que puedo contribuir con algo.

Siempre puedes hacer algo como esto:

 package com.dyna.test; import java.io.File; import java.lang.reflect.Constructor; public class DynamicClass{ @SuppressWarnings("unchecked") public Object castDynamicClass(String className, String value){ Class dynamicClass; try { //We get the actual .class object associated with the specified name dynamicClass = Class.forName(className); /* We get the constructor that received only a String as a parameter, since the value to be used is a String, but we could easily change this to be "dynamic" as well, getting the Constructor signature from the same datasource we get the values from */ Constructor cons = (Constructor) dynamicClass.getConstructor(new Class[]{String.class}); /*We generate our object, without knowing until runtime what type it will be, and we place it in an Object as any Java object extends the Object class) */ Object object = (Object) cons.newInstance(new Object[]{value}); return object; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { DynamicClass dynaClass = new DynamicClass(); /* We specify the type of class that should be used to represent the value "3.0", in this case a Double. Both these parameters you can get from a file, or a network stream for example. */ System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0")); /* We specify a different value and type, and it will work as expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and File.toString() would do. */ System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath")); } 

Por supuesto, esto no es realmente un casting dynamic, como en otros lenguajes (Python, por ejemplo), porque java es una lengua estáticamente estática. Sin embargo, esto puede resolver algunos casos marginales donde realmente necesita cargar algunos datos de diferentes maneras, dependiendo de algún identificador. Además, la parte donde se obtiene un constructor con un parámetro de cadena podría ser más flexible, haciendo pasar ese parámetro desde la misma fuente de datos. Es decir, desde un archivo, obtienes la firma del constructor que deseas usar, y la lista de valores que se usarán, de esa manera emparejas, por ejemplo, el primer parámetro es una Cadena, con el primer objeto, lanzándolo como una Cadena, El siguiente objeto es un Entero, etc., pero si estuviera a lo largo de la ejecución de su progtwig, primero obtendrá un objeto Archivo, luego un Doble, etc.

De esta manera, puedes dar cuenta de esos casos y hacer un casting un tanto “dynamic” sobre la marcha.

Espero que esto ayude a cualquiera ya que esto sigue apareciendo en las búsquedas de Google.

Hace poco sentí que tenía que hacer esto también, pero luego encontré otra manera que posiblemente hace que mi código luzca más ordenado y usa un mejor OOP.

Tengo muchas clases de hermanos que implementan un cierto método doSomething() . Para acceder a ese método, primero tendría que tener una instancia de esa clase, pero creé una superclase para todas mis clases de hermanos y ahora puedo acceder al método desde la superclase.

A continuación, muestro dos formas alternativas de “fundición dinámica”.

 // Method 1. mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); switch (mUnitNum) { case 0: ((MyFragment0) mFragment).sortNames(sortOptionNum); break; case 1: ((MyFragment1) mFragment).sortNames(sortOptionNum); break; case 2: ((MyFragment2) mFragment).sortNames(sortOptionNum); break; } 

y mi método actualmente usado,

 // Method 2. mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); mSuperFragment.sortNames(sortOptionNum); 

Solo pensé en publicar algo que encontré bastante útil y podría ser posible para alguien que experimenta necesidades similares.

El siguiente método fue un método que escribí para mi aplicación JavaFX para evitar tener que enviar y también evitar escribir si el objeto x instancia de las instrucciones del objeto b cada vez que se devolvió el controlador.

 public  Optional getController(Class castKlazz){ try { return Optional.of(fxmlLoader.getController()); }catch (Exception e){ e.printStackTrace(); } return Optional.empty(); } 

La statement del método para obtener el controlador fue

 public  T getController() 

Al usar el tipo U pasado a mi método a través del objeto de la clase, podría reenviarse al método get controller para indicarle qué tipo de objeto devolver. Se devuelve un objeto opcional en caso de que se suministre una clase incorrecta y se produzca una excepción, en cuyo caso se devolverá un elemento opcional vacío que podemos verificar.

Así es como se veía la llamada final al método (si el presente del objeto opcional devuelto tiene un Consumidor)

 getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete()); 

Prueba esto para Dynamic Casting. ¡¡¡Funcionará!!!

  String something = "1234"; String theType = "java.lang.Integer"; Class theClass = Class.forName(theType); Constructor cons = theClass.getConstructor(String.class); Object ob = cons.newInstance(something); System.out.println(ob.equals(1234)); 
    Intereting Posts