java: ¿cómo puedo hacer un casting dinámico de una variable de un tipo a otro?


Me gustaría hacer un casting dinámico para una variable java, el tipo de casting 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 tratando de llenar una clase con un HashMap que he recibido.

Este es el constructor:

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> 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 clases son de tipo Doble, y si el número 3 es recibido lo ve como Entero y tengo problema de tipo.

Author: ufk, 2010-01-24

13 answers

Con respecto a su actualización, la única manera de resolver esto en Java es escribir código que cubra todos los casos con muchas expresiones if y else y instanceof. Lo que intenta hacer parece como si se utilizan para programar con lenguajes dinámicos. En lenguajes estáticos, lo que intentas hacer es casi imposible y uno probablemente elegiría un enfoque totalmente diferente para lo que intentas hacer. Los lenguajes estáticos no son tan flexibles como los dinámicos:)

Buenos ejemplos de Java las mejores prácticas son la respuesta de BalusC (ie ObjectConverter) y la respuesta de Andreas_D (ie 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 dinámico para este tipo estático.

PS: La primera línea de su ejemplo podría escribirse como Class<String> stringClass = String.class; pero aún así, no puede usar stringClass para convertir variables.

 12
Author: akuhn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:20

Sí es posible usar Reflexión

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 dada, puede simplemente convertir a esa clase.

Si desea obtener una clase dada, Número por ejemplo:

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

Pero todavía no tiene sentido hacerlo así, solo podría lanzar a Número.

Fundición de un objeto NO cambia nada; es solo el el compilador lo trata.
La única razón para hacer algo así, es comprobar 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().

Actualización

De acuerdo con su última actualización el verdadero problema es que tiene un Entero en su HashMap que debe asignarse a un Doble. Lo que puede hacer en este caso, es comprobar el tipo del campo y utilizar los métodos xxxValue() de Número

...
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 de si me gusta la idea de tener el tipo incorrecto en el Mapa)

 90
Author: Carlos Heuberger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-17 15:45:36

Necesitarás 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 objetivo por Field#getDeclaringClass().

Puedes encontrar aquí un ejemplo de tal ObjectConverter. Debería darte una idea básica. Si desea más posibilidades de conversión, simplemente agregue más métodos con el argumento deseado y el tipo de retorno.

 20
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-24 14:29:05

Puede hacer esto usando el Class.cast() método, que envía dinámicamente el parámetro suministrado al tipo de la instancia de clase que tiene. Para obtener la instancia de clase de un campo en particular, se utiliza el getType() método 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 usarse sin modificar.

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> 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);
    }
}
 11
Author: Jared Russell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-24 14:57:01

Funciona e incluso hay un patrón común para su enfoque: el patrón de adaptador . Pero por supuesto, (1) no funciona para lanzar primitivas java a objetos y (2) la clase tiene que ser adaptable (generalmente 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 se puede usar un valor de cadena para el casting.

 4
Author: Andreas_D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-24 14:29:32

Puedes escribir un método de fundición simple como el de abajo.

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

En tu método deberías usarlo como

public ConnectParams(HashMap<String,Object> object) {

for (Map.Entry<String, Object> 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());          
    }    
}

}
 3
Author: Hoji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-28 07:04:25

Su problema no es la falta de "casting dinámico". Casting Integer a Double no es posible en absoluto. Parece que desea darle a Java un objeto de un tipo, un campo de un tipo posiblemente incompatible, y hacer que de alguna manera averigüe automáticamente cómo convertir entre los tipos.

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

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

 2
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-24 14:29:48

No hagas esto. Solo tienes que tener un constructor correctamente parametrizado en su lugar. El conjunto y los tipos de los parámetros de conexión son fijos de todos modos, por lo que no tiene sentido hacerlo todo dinámicamente.

 1
Author: Bandi-T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-24 14:43:57

Por si sirve de algo, la mayoría de los lenguajes de scripting (como Perl) y los lenguajes no estáticos en tiempo de compilación (como Pick) admiten conversiones automáticas de cadenas dinámicas en tiempo de ejecución a objetos (relativamente arbitrarias). Esto también se puede lograr en Java sin perder la seguridad de tipo y las cosas buenas que proporcionan los lenguajes de tipo estático SIN los desagradables efectos secundarios de algunos de los otros lenguajes que hacen cosas malas con dynamic casting. Un ejemplo de Perl que hace algo cuestionable matemáticas:

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

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

Object fromString (String value, Class targetClass)

Desafortunadamente, no hay métodos Java integrados 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 soportante. Por mi parte, el punto es proporcionar una forma totalmente dinámica de lograr esto - para lo cual no creo que la referencia previa sea el enfoque correcto-tener que codificar cada conversión. En pocas palabras, la implementación es solo cast-from-string si es legal/posible.

Así que la solución es simple reflexión buscando miembros públicos de cualquiera de los dos:

STRING_CLASS_ARRAY = (nueva clase [] {String.clase});

A) Member member = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Miembro miembro = targetClass.En este caso, el valor de los datos se calculará de la siguiente manera:]}

Encontrará que todas las primitivas (Integer, Long, etc.) y todos los conceptos básicos (BigInteger, BigDecimal, etc.) e incluso java.regex.Todos los patrones están cubiertos a través de este enfoque. He utilizado esto con un éxito significativo en proyectos de producción donde hay una gran cantidad de entradas de valores de cadena arbitrarias donde se necesitaba una verificación más estricta. En este enfoque, si no hay ningún método o cuando se invoca el método, se lanzado (porque es un valor ilegal, como una entrada no numérica a una expresión regular BigDecimal o ilegal para un Patrón), que proporciona la comprobación específica de la lógica inherente de la clase objetivo.

Esto tiene algunas desventajas:

1) Es necesario entender bien la reflexión (esto es un poco complicado y no para novatos). 2) Algunas de las clases Java y de hecho las bibliotecas de 3rd-party (sorpresa) no están codificadas correctamente. Es decir, hay métodos que toman un solo argumento de cadena como ingrese y devuelva una instancia de la clase de destino, pero no es lo que piensa... Considere la clase Integer:

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 ints primitivos. Reflection encontrará esto como un posible candidato para crear un Entero a partir de una Cadena incorrectamente en comparación con los miembros decode, valueof y constructor, que son todos adecuados para la mayoría de las conversiones de cadenas arbitrarias donde realmente no tiene control sobre sus datos de entrada, quiero 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 tales objetos deberían lanzar una Excepción. Desafortunadamente, las implementaciones varían en cuanto a si las excepciones se declaran como verificadas o no. Entero.valueOf (String) lanza una excepción NumberFormatException marcada, por ejemplo, pero Pattern.las excepciones de compile () no se encuentran durante las búsquedas de reflexión. Una vez más, no un el fracaso de este enfoque dinámico de "cross-casting" creo que es una implementación muy no estándar para las declaraciones de excepción en los métodos de creación de objetos.

Si alguien desea 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 lo mejor que podamos para usarlo correctamente.

Saludos.

 1
Author: Darrell Teague,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-27 16:59:24

Por lo tanto, este es un post antiguo, sin embargo, creo que puedo contribuir algo a él.

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 dinámico, como en otros lenguajes (Python por ejemplo), porque java es un lang de tipo estático. Sin embargo, esto puede resolver algunos casos marginales en los que 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 probablemente se hizo más flexible, al tener ese parámetro pasado de la misma fuente de datos. Es decir, a partir de un archivo, se obtiene la firma del constructor que desea utilizar, y la lista de valores que se utilizarán, de esa manera se empareja, por ejemplo, el primer parámetro es una cadena, con el primer objeto, fundiéndolo como una Cadena, el siguiente objeto es un Entero, etc., pero hubo algo a lo largo de la ejecución de su programa, ahora se obtiene un objeto de archivo primero, luego un Doble, etc.

De esta manera, puede dar cuenta de esos casos, y hacer un casting algo" dinámico " sobre la marcha.

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

 1
Author: Acapulco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-07 15:02:54

Recientemente sentí que tenía que hacer esto también, pero luego encontré otra forma que posiblemente hace que mi código se vea más limpio y use mejor OOP.

Tengo muchas clases de hermanos que cada uno implementa 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 hermanas y ahora puedo acceder al método desde la superclase.

A continuación muestro dos formas alternativas a "dinámica fundición".

// 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 utilizado,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);
 0
Author: Anonsage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-16 21:09:01

Solo pensé en publicar algo que me pareció muy útil y que 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 emitir y también evitar escribir si object x instancia de sentencias object b cada vez que se devolvía el controlador.

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

La declaración del método para obtener el controlador fue

public <T> T getController()

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

Así es como se veía la llamada final al método (si está presente el objeto opcional devuelto toma un Consumidor

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());
 0
Author: Eladian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-07 01:04:23

Pruebe esto para el Casting dinámico. 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));
 0
Author: Sunil M M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-27 02:33:39