Habiendo aprendido durante mis días en C ++ sobre los males del operador de conversión de estilo C, al principio me complació descubrir que en Java 5 java.lang.Class
había adquirido un cast
método.
Pensé que finalmente tenemos una forma OO de lidiar con el casting.
Resulta que Class.cast
no es lo mismo que static_cast
en C ++. Es más como reinterpret_cast
. No generará un error de compilación donde se espera, sino que se aplazará hasta el tiempo de ejecución. Aquí hay un caso de prueba simple para demostrar diferentes comportamientos.
package test;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestCast
{
static final class Foo
{
}
static class Bar
{
}
static final class BarSubclass
extends Bar
{
}
@Test
public void test ( )
{
final Foo foo = new Foo( );
final Bar bar = new Bar( );
final BarSubclass bar_subclass = new BarSubclass( );
{
final Bar bar_ref = bar;
}
{
// Compilation error
final Bar bar_ref = foo;
}
{
// Compilation error
final Bar bar_ref = (Bar) foo;
}
try
{
// !!! Compiles fine, runtime exception
Bar.class.cast( foo );
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
{
final Bar bar_ref = bar_subclass;
}
try
{
// Compiles fine, runtime exception, equivalent of C++ dynamic_cast
final BarSubclass bar_subclass_ref = (BarSubclass) bar;
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
}
}
Entonces, estas son mis preguntas.
- ¿Debería
Class.cast()
ser desterrado a tierras genéricas? Allí tiene bastantes usos legítimos. - ¿Deberían los compiladores generar errores de compilación cuando
Class.cast()
se utiliza y se pueden determinar condiciones ilegales en el momento de la compilación? - ¿Debería Java proporcionar un operador de conversión como una construcción de lenguaje similar a C ++?
java
generics
casting
compiler-warnings
Alexander Pogrebnyak
fuente
fuente
Class.cast()
cuando se pueden determinar condiciones ilegales en tiempo de compilación. En ese caso, todos, excepto usted, solo usan el operador de conversión estándar. (3) Java tiene un operador de conversión como construcción de lenguaje. No es similar a C ++. Esto se debe a que muchas de las construcciones del lenguaje Java no son similares a C ++. A pesar de las similitudes superficiales, Java y C ++ son bastante diferentes.Respuestas:
Solo he usado
Class.cast(Object)
para evitar advertencias en "tierras genéricas". A menudo veo métodos que hacen cosas como esta:A menudo es mejor reemplazarlo por:
Ese es el único caso de uso con el que
Class.cast(Object)
me he encontrado.Con respecto a las advertencias del compilador: sospecho que
Class.cast(Object)
no es especial para el compilador. Podría optimizarse cuando se usa estáticamente (es decir, enFoo.class.cast(o)
lugar decls.cast(o)
) pero nunca he visto a nadie usarlo, lo que hace que el esfuerzo de construir esta optimización en el compilador sea algo inútil.fuente
En primer lugar, se le desaconseja encarecidamente hacer casi cualquier yeso, por lo que debe limitarlo tanto como sea posible. Pierde los beneficios de las funciones fuertemente tipadas en tiempo de compilación de Java.
En cualquier caso,
Class.cast()
debe usarse principalmente cuando recupera elClass
token mediante reflexión. Es más idiomático escribirmás bien que
EDITAR: Errores en tiempo de compilación
Sobre todo, Java realiza comprobaciones de conversión solo en tiempo de ejecución. Sin embargo, el compilador puede emitir un error si puede probar que tales conversiones nunca pueden tener éxito (por ejemplo, lanzar una clase a otra clase que no es un supertipo y lanzar un tipo de clase final a la clase / interfaz que no está en su jerarquía de tipos). Aquí, dado que
Foo
yBar
hay clases que no están en la jerarquía de las otras, el elenco nunca puede tener éxito.fuente
Class.cast
parece encajar en la factura, crea más problemas de los que resuelve.Siempre es problemático y a menudo engañoso intentar traducir construcciones y conceptos entre idiomas. El casting no es una excepción. Particularmente porque Java es un lenguaje dinámico y C ++ es algo diferente.
Toda la transmisión en Java, no importa cómo lo hagas, se realiza en tiempo de ejecución. La información de tipo se mantiene en tiempo de ejecución. C ++ es un poco más una mezcla. Puede convertir una estructura en C ++ a otra y es simplemente una reinterpretación de los bytes que representan esas estructuras. Java no funciona de esa manera.
Además, los genéricos en Java y C ++ son muy diferentes. No se preocupe demasiado por cómo hace las cosas de C ++ en Java. Necesita aprender a hacer las cosas a la manera de Java.
fuente
(Bar) foo
, genera un error en el momento de la compilación, peroBar.class.cast(foo)
no lo hace. En mi opinión, si se usa de esta manera, debería.Bar.class.cast(foo)
le dice explícitamente al compilador que desea realizar la conversión en tiempo de ejecución. Si desea una verificación en tiempo de compilación de la validez del elenco, su única opción es hacer el(Bar) foo
elenco de estilo.Class.cast()
rara vez se usa en código Java. Si se usa, generalmente con tipos que solo se conocen en tiempo de ejecución (es decir, a través de sus respectivosClass
objetos y por algún parámetro de tipo). Solo es realmente útil en código que usa genéricos (esa es también la razón por la que no se introdujo antes).Es no similar a
reinterpret_cast
, ya que no permitirá romper el sistema de tipos en tiempo de ejecución más que un reparto normal no (es decir, se puede romper parámetros de tipo genérico, pero no puede romper tipos "reales").Los males del operador de conversión de estilo C generalmente no se aplican a Java. El código Java que se parece a una conversión de estilo C es más similar a un tipo
dynamic_cast<>()
con un tipo de referencia en Java (recuerde: Java tiene información de tipo de tiempo de ejecución).En general, comparar los operadores de conversión de C ++ con la conversión de Java es bastante difícil, ya que en Java solo puede emitir referencias y no se produce ninguna conversión en los objetos (solo los valores primitivos se pueden convertir utilizando esta sintaxis).
fuente
dynamic_cast<>()
con un tipo de referencia.En general, se prefiere el operador de conversión al método de conversión de Clase #, ya que es más conciso y el compilador puede analizarlo para detectar problemas evidentes con el código.
Class # cast asume la responsabilidad de la verificación de tipos en tiempo de ejecución en lugar de durante la compilación.
Ciertamente, existen casos de uso para Class # cast, particularmente cuando se trata de operaciones reflectivas.
Desde que lambda llegó a Java, personalmente me gusta usar Class # cast con la API de colecciones / flujo si estoy trabajando con tipos abstractos, por ejemplo.
fuente
C ++ y Java son lenguajes diferentes.
El operador de conversión de estilo Java C es mucho más restringido que la versión C / C ++. Efectivamente, la transmisión de Java es como la transmisión dinámica de C ++ si el objeto que tiene no se puede convertir a la nueva clase, obtendrá una excepción de tiempo de ejecución (o si hay suficiente información en el código, un tiempo de compilación). Por lo tanto, la idea de C ++ de no usar conversiones de tipo C no es una buena idea en Java
fuente
Además de eliminar las advertencias de transmisión fea como la mayoría de las menciones, Class.cast es una transmisión en tiempo de ejecución que se usa principalmente con una transmisión genérica, debido a que la información genérica se borrará en el tiempo de ejecución y de alguna manera cada genérico se considerará Objeto, esto lleva a no lanzar una ClassCastException temprana.
por ejemplo serviceLoder use este truco al crear los objetos, marque S p = service.cast (c.newInstance ()); esto arrojará una excepción de conversión de clase cuando SP = (S) c.newInstance (); no lo hará y puede mostrar una advertencia 'Tipo de seguridad: conversión sin marcar de Objeto a S' . (igual que Objeto P = (Objeto) c.newInstance ();)
-simplemente comprueba que el objeto emitido es una instancia de la clase de conversión, luego usará el operador de conversión para transmitir y ocultar la advertencia suprimiéndola.
Implementación de Java para transmisión dinámica:
fuente
Personalmente, he usado esto antes para construir un convertidor de JSON a POJO. En el caso de que el JSONObject procesado con la función contenga una matriz o JSONObjects anidados (lo que implica que los datos aquí no son de un tipo primitivo o
String
), intento invocar el método setter usandoclass.cast()
de esta manera:No estoy seguro de si esto es extremadamente útil, pero como se dijo aquí antes, la reflexión es uno de los pocos casos de uso legítimos en los
class.cast()
que puedo pensar, al menos ahora tienes otro ejemplo.fuente