objeto - tipos de variables en java ejemplos
java: ¿cómo puedo hacer fundición dinámica de una variable de un tipo a otro? (13)
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
yelse
einstanceof
. Lo que intenta hacer se ve como si se utilizara para programar con lenguajes dinámicos. 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 dinámicos :)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 dinámico para este tipo estático.
PD: la primera línea de su ejemplo podría escribirse como Class<String> stringClass = String.class;
pero aún así, no puedes usar stringClass
para moldear variables.
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<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 clase son de tipo Doble, y si se recibe el número 3, lo ve como Entero y tengo un problema de tipo.
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 dinámico, 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 programa, primero obtendrá un objeto Archivo, luego un Doble, etc.
De esta manera, puedes dar cuenta de esos casos y hacer un casting un tanto "dinámico" sobre la marcha.
Espero que esto ayude a cualquiera ya que esto sigue apareciendo en las búsquedas de Google.
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.
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);
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 compilació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 dinámico. 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 objetivo.
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 objetivo, 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 dinámico 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.
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));
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<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);
}
}
Puedes escribir un método de cast 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());
}
}
}
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<? extends Number> 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 compilador 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)
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 <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()
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());
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 objetivo por Field#getDeclaringClass()
.
Puede encontrar here 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.
Tu problema no es la falta de "casting dinámico". 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.