Conversión de variables en Java

84

Me pregunto si alguien podría decirme cómo funciona el casting. Entiendo cuándo debería hacerlo, pero no realmente cómo funciona. En los tipos de datos primitivos, entiendo parcialmente, pero cuando se trata de lanzar objetos, no entiendo cómo funciona.

¿Cómo se puede convertir un objeto con el tipo Objeto de repente, digamos, MyType(solo un ejemplo) y luego obtener todos los métodos?

usuario626912
fuente
Lectura sugerida: Herencia
Francesco Menzani

Respuestas:

182

Transmitir en Java no es mágico, le dice al compilador que un Objeto de tipo A es en realidad de tipo B más específico y, por lo tanto, obtiene acceso a todos los métodos en B que de otra manera no habría tenido. No está realizando ningún tipo de magia o conversión al realizar un casting, esencialmente le está diciendo al compilador "confía en mí, sé lo que estoy haciendo y puedo garantizarle que este Objeto en esta línea es en realidad un <Insertar escriba aquí>. " Por ejemplo:

Object o = "str";
String str = (String)o;

Lo anterior está bien, no es mágico y todo está bien. El objeto que se almacena en o es en realidad una cadena y, por lo tanto, podemos convertir a una cadena sin ningún problema.

Hay dos formas en que esto podría salir mal. En primer lugar, si está lanzando entre dos tipos en jerarquías de herencia completamente diferentes, el compilador sabrá que está siendo tonto y lo detendrá:

String o = "str";
Integer str = (Integer)o; //Compilation fails here

En segundo lugar, si están en la misma jerarquía pero siguen siendo un elenco no válido, se ClassCastExceptionlanzará un en tiempo de ejecución:

Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here

Esto esencialmente significa que ha violado la confianza del compilador. Le ha dicho que puede garantizar que el objeto es de un tipo en particular, y no lo es.

¿Por qué necesitas un casting? Bueno, para empezar solo lo necesitas cuando pasas de un tipo más general a un tipo más específico. Por ejemplo, Integerhereda de Number, por lo que si desea almacenar un Integercomo a, Numberentonces está bien (ya que todos los enteros son números) .Sin embargo, si desea ir al revés, necesita un reparto; no todos los números son enteros (también como entero que tenemos Double, Float, Byte, Long, etc.) e incluso si sólo hay una subclase en su proyecto o el JDK, alguien podría fácilmente crear otro y distribuir eso, así que usted no tiene ninguna garantía, incluso si usted piensa que es una sola opción, obvia !

Con respecto al uso para la transmisión, todavía ve la necesidad de hacerlo en algunas bibliotecas. Antes de Java-5 se usaba mucho en colecciones y varias otras clases, ya que todas las colecciones trabajaban para agregar objetos y luego emitir el resultado que obtuviste de la colección. Sin embargo, con la llegada de los genéricos, gran parte del uso del casting ha desaparecido: ha sido reemplazado por genéricos que brindan una alternativa mucho más segura, sin el potencial de ClassCastExceptions (de hecho, si usa genéricos de manera limpia y se compila sin advertencias, tiene la garantía de que nunca obtendrá una ClassCastException).

Michael Berry
fuente
Gracias por su explicación. Si entiendo esto correctamente, probablemente no lo entiendo, cuando lanza un objeto, simplemente le está diciendo al compilador que sé que el objeto en esta dirección de memoria sabe cómo responder a estos métodos, etc., ¿así que no me niegue? ¿Es eso correcto?
user626912
1
@ user626912 Algo así: no lo pienses en términos de direcciones de memoria. No solo le está diciendo al compilador que el objeto dado se ajusta a una interfaz y, por lo tanto, tiene implementaciones de métodos dados (podría crear un objeto totalmente diferente con los mismos métodos y la conversión no funcionaría necesariamente). Le está diciendo al compilador que el objeto de un tipo es en realidad un tipo más específico y, por lo tanto, puede utilizar los métodos disponibles en ese objeto más específico. Lea sobre el polimorfismo si aún no lo ha hecho, podría ayudar a aclarar algunas cosas.
Michael Berry
Dudo de su afirmación "sólo la necesita cuando pasa de un tipo más general a un tipo más específico". ((Object) gpsLastLoc.getLatitude ()). GetClass (). GetSimpleName () devolverá el nombre "doble" en el tiempo de ejecución, y este es el ejemplo de uso de conversión de un tipo más específico a un tipo más general.
Fruta
1
@ 林果 皞 En este caso, solo estás usando casting para promover una primitiva; esto no es lo mismo que ir a un tipo más general, y una forma muy extraña de hacer las cosas. La forma más normal (mejor) sería Double.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName(). En cualquier caso, nunca debería necesitar obtener la clase de una primitiva dinámicamente, ya que si getLatitude()devuelve una primitiva doble, siempre sabrá que se promoverá a un Doubleobjeto.
Michael Berry
Me encanta la forma en que lo pones, que el código ha "violado la confianza del compilador". (Ps. Tienes un error tipográfico, escribiste "eres" en lugar de "tienes".)
Jason L.
7

En realidad, el casting no siempre funciona. Si el objeto no es una instanceofclase a la que lo estás enviando, obtendrás un ClassCastExceptiontiempo de ejecución.

sandrstar
fuente
5

Suponga que desea convertir a Stringa a File(sí, no tiene ningún sentido), no puede convertirlo directamente porque la Fileclase no es un hijo ni un padre de la Stringclase (y el compilador se queja).

Pero podrías lanzar tu Stringto Object, porque a Stringes un Object( Objectes padre). Entonces podría convertir este objeto a a File, porque un archivo es un Object.

Así que todas sus operaciones son 'legales' desde el punto de vista de la escritura en tiempo de compilación, ¡pero eso no significa que funcionará en tiempo de ejecución!

File f = (File)(Object) "Stupid cast";

El compilador permitirá esto incluso si no tiene sentido, pero se bloqueará en tiempo de ejecución con esta excepción:

Exception in thread "main" java.lang.ClassCastException:
    java.lang.String cannot be cast to java.io.File
Christophe Roussy
fuente
3

Enviar una referencia solo funcionará si es de instanceofese tipo. No puedes emitir referencias al azar. Además, necesita leer más sobre Casting Objects.

p.ej

String string = "String";

Object object = string; // Perfectly fine since String is an Object

String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.
pregunta
fuente
3

La forma correcta es esta:

Integer i = Integer.class.cast(obj);

El método cast()es una alternativa mucho más segura a la conversión en tiempo de compilación.

yegor256
fuente