En la programación basada en clases , la conversión descendente , o refinamiento de tipo, consiste en convertir una referencia de clase base o padre a una referencia de clase derivada más restringida. [ 1 ] Esto solo es posible si el objeto ya es una instancia de la clase derivada, por lo que esta conversión es inherentemente falible. En cambio, la conversión ascendente , que consiste en tratar explícitamente un objeto como si fuera una instancia de una de sus superclases, siempre es posible. [ 2 ]
En muchos entornos, la introspección de tipos se puede utilizar para obtener el tipo de una instancia de objeto en tiempo de ejecución y, a continuación, utilizar este resultado para evaluar explícitamente su compatibilidad de tipos con otro tipo. Los posibles resultados de la comparación de tipos polimórficos —además de que sean equivalentes (idénticos) o no relacionados (incompatibles)— incluyen dos casos adicionales: a saber, cuando el primer tipo se deriva del segundo, y luego lo mismo pero intercambiado en sentido contrario (véase: Subtipado § Subsunción ).
Con esta información, un programa puede comprobar, antes de realizar una operación como almacenar un objeto en una variable tipada, si dicha operación es segura en cuanto a tipos o si produciría un error. Si el tipo de la instancia en tiempo de ejecución deriva del tipo de la variable de destino (es decir, de la variable padre), es posible realizar una conversión descendente.
Algunos lenguajes, como OCaml , no permiten la conversión descendente. [ 3 ]
Ejemplos
Java
public class Fruit {} // clase padre public class Apple extends Fruit {} // clase hijapublic static void main ( String [] args ) { // Lo siguiente es una conversión ascendente implícita: Fruit parent = new Apple (); // Lo siguiente es una conversión descendente. Aquí funciona ya que la variable `parent` // contiene una instancia de Apple: Apple child = ( Apple ) parent ; }C++
// Clase padre: class Fruit { public : // Debe ser polimórfica para usar la conversión dinámica con comprobación en tiempo de ejecución. virtual ~ Fruit () = default ; };// Clase hija: class Apple : public Fruit {};int main ( int argc , char * argv []) { // Lo siguiente es una conversión ascendente implícita: Fruit * parent = new Apple (); // Lo siguiente es una conversión descendente. Aquí funciona ya que la variable `parent` // contiene una instancia de Apple: Apple * child = dynamic_cast < Apple *> ( parent ); delete parent ; }Usos
La conversión descendente es útil cuando se conoce el tipo del valor al que hace referencia la variable principal y se usa con frecuencia al pasar un valor como parámetro. En el siguiente ejemplo, el método objectToString recibe un parámetro de tipo Object, que se supone que es de tipo String.
public static String objectToString ( Object myObject ) { // Esto solo funcionará cuando el valor que contiene actualmente myObject sea una cadena. return ( String ) myObject ; }public static void main ( String [] args ) { // Esto funcionará ya que pasamos una cadena, por lo que myObject tiene un valor de tipo cadena. String result = objectToString ( "Mi cadena" ); Object iFail = new Object (); // Esto fallará ya que pasamos un objeto que no tiene un valor de tipo cadena. result = objectToString ( iFail ); }En este enfoque, la conversión descendente impide que el compilador detecte un posible error y, en su lugar, provoca un error en tiempo de ejecución. La conversión descendente de myObject a String ('(String)myObject') no era posible en tiempo de compilación porque hay ocasiones en que myObject es de tipo String, por lo que solo en tiempo de ejecución podemos determinar si el parámetro pasado es lógico. Si bien también podríamos convertir myObject a un String en tiempo de compilación usando la función universal java.lang.Object.toString(), esto implicaría el riesgo de llamar a la implementación predeterminada de toString() donde no sería útil o sería insegura, y el manejo de excepciones no podría evitarlo.
En C++, la comprobación de tipos en tiempo de ejecución se implementa mediante `dynamic_cast` . La conversión descendente en tiempo de compilación se implementa mediante `static_cast` , pero esta operación no realiza ninguna comprobación de tipos. Si se utiliza incorrectamente, podría producir un comportamiento indefinido.
Consideraciones
Un ejemplo común de diseño mal concebido son los contenedores de tipos superiores , como los contenedores de Java antes del lanzamiento de los genéricos de Java , que requieren la conversión descendente de los objetos contenidos para poder volver a utilizarlos.
Véase también
Referencias
- ↑ TylerMSFT (03/08/2021). "Cómo: Usar safe_cast en C++/CLI" . learn.microsoft.com . Consultado el 01/12/2023 .
- ↑ Liang, Y. Daniel (2015). Introducción a la programación Java: Versión completa (10.ª ed.). Pearson. pág. 427. ISBN 978-0-13-376131-3.
- ↑ Vouillon, Jérôme; Rémy, Didier; Garrigue, Jacques (12 de septiembre de 2013). "Objetos en OCaml" . El sistema OCaml versión 4.01 : Documentación y manual del usuario .
Enlaces externos
- El downcasting es un olor a código, por Jeremy D. Miller
- Una tragedia desoladora de Jimmy Bogard
- Prefiero el polimorfismo a la instancia de y la conversión descendente, por Bill Venners.
- Conversión descendente en C# por Scott Lysle
- Múltiples técnicas de lanzamiento descendente
- Lanzamiento ascendente, lanzamiento descendente por Sinipull
- Clase (programación informática)