Saltar al contenido

¿Covarianza, invarianza y contravarianza explicadas en un lenguaje sencillo?

Este equipo de especialistas despúes de varios días de investigación y de juntar de información, encontramos la solución, nuestro deseo es que resulte útil para ti en tu proyecto.

Solución:

Algunos dicen que se trata de la relación entre tipos y subtipos, otros dicen que se trata de conversión de tipos y otros dicen que se usa para decidir si un método se sobrescribe o se sobrecarga.

Todo lo anterior.

En el fondo, estos términos describen cómo la relación de subtipo se ve afectada por las transformaciones de tipo. Es decir, si A y B son tipos, f es una transformación de tipo, y ≤ la relación de subtipo (es decir A ≤ B significa que A es un subtipo de B), tenemos

  • f es covariante si A ≤ B implica que f(A) ≤ f(B)
  • f es contravariante si A ≤ B implica que f(B) ≤ f(A)
  • f es invariante si ninguno de los anteriores se cumple

Consideremos un ejemplo. Dejar f(A) = List dónde List es declarado por

class List  ...  

Es f covariante, contravariante o invariante? Covariante significaría que un List es un subtipo de List, contravariante que un List es un subtipo de List e invariante que ninguno es un subtipo del otro, es decir List y List son tipos inconvertibles. En Java, este último es true, decimos (algo informalmente) que genéricos son invariantes.

Otro ejemplo. Dejar f(A) = A[]. Es f covariante, contravariante o invariante? Es decir, es String[] un subtipo de objeto[], Objeto[] un subtipo de String[]¿O no es un subtipo del otro? (Respuesta: en Java, las matrices son covariantes)

Esto todavía era bastante abstracto. Para hacerlo más concreto, veamos qué operaciones en Java se definen en términos de la relación de subtipo. El ejemplo más simple es la asignación. La declaración

x = y;

se compilará solo si typeof(y) ≤ typeof(x). Es decir, acabamos de enterarnos de que las declaraciones

ArrayList strings = new ArrayList();
ArrayList objects = new ArrayList();

no se compilará en Java, pero

Object[] objects = new String[1];

voluntad.

Otro ejemplo en el que la relación de subtipo es importante es una expresión de invocación de método:

result = method(a);

Hablando informalmente, esta afirmación se evalúa asignando el valor de a al primer parámetro del método, luego ejecutando el cuerpo del método, y luego asignando el valor de retorno del método a result. Como la asignación simple en el último ejemplo, el “lado derecho” debe ser un subtipo del “lado izquierdo”, es decir, esta declaración solo puede ser válida si typeof(a) ≤ typeof(parameter(method)) y returntype(method) ≤ typeof(result). Es decir, si el método es declarado por:

Number[] method(ArrayList list)  ... 

ninguna de las siguientes expresiones se compilará:

Integer[] result = method(new ArrayList());
Number[] result = method(new ArrayList());
Object[] result = method(new ArrayList());

pero

Number[] result = method(new ArrayList());
Object[] result = method(new ArrayList());

voluntad.

Otro ejemplo en el que la subtipificación es importante es primordial. Considerar:

Super sup = new Sub();
Number n = sup.method(1);

dónde

class Super 
    Number method(Number n)  ... 


class Sub extends Super 
    @Override 
    Number method(Number n);

De manera informal, el tiempo de ejecución reescribirá esto en:

class Super 
    Number method(Number n) 
        if (this instanceof Sub) 
            return ((Sub) this).method(n);  // *
         else 
            ... 
        
    

Para que la línea marcada se compile, el parámetro de método del método reemplazado debe ser un supertipo del parámetro de método del método reemplazado, y el tipo de retorno un subtipo del método reemplazado. Hablando formalmente, f(A) = parametertype(method asdeclaredin(A)) debe ser al menos contravariante, y si f(A) = returntype(method asdeclaredin(A)) debe ser al menos covariante.

Tenga en cuenta el “al menos” de arriba. Esos son requisitos mínimos que cualquier lenguaje de programación orientado a objetos seguro de tipo estático razonable hará cumplir, pero un lenguaje de programación puede optar por ser más estricto. En el caso de Java 1.4, los tipos de parámetros y los tipos de retorno de métodos deben ser idénticos (excepto para el borrado de tipos) cuando se anulan los métodos, es decir parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B)) al anular. Desde Java 1.5, los tipos de retorno covariantes están permitidos al anular, es decir, lo siguiente se compilará en Java 1.5, pero no en Java 1.4:

class Collection 
    Iterator iterator()  ... 


class List extends Collection 
    @Override 
    ListIterator iterator()  ... 

Espero haber cubierto todo, o mejor dicho, haber rayado la superficie. Aún así, espero que ayude a comprender el concepto abstracto, pero importante, de la varianza de tipos.

Tomando el sistema de tipos de Java, y luego las clases:

Cualquier objeto de algún tipo T puede sustituirse por un objeto de subtipo de T.

VARIENCIA DE TIPO: LOS MÉTODOS DE CLASE TIENEN LAS SIGUIENTES CONSECUENCIAS

class A 
    public S f(U u)  ... 


class B extends A 
    @Override
    public T f(V v)  ... 


B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;

Puede observarse que:

  • La T debe ser subtipo S (covariante, ya que B es subtipo de A).
  • La V debe ser supertipo de U (contravariante, como dirección de herencia contraria).

Ahora co- y contra- se relacionan con B como subtipo de A. Los siguientes tipos más fuertes pueden introducirse con un conocimiento más específico. En el subtipo.

La covarianza (disponible en Java) es útil, para decir que uno devuelve un resultado más específico en el subtipo; especialmente visto cuando A = T y B = S. La contravarianza dice que está preparado para manejar un argumento más general.

La varianza se trata de relaciones entre clases con diferentes parámetros genéricos. Sus relaciones son la razón por la que podemos elegirlos.

La variación Co y Contra son cosas bastante lógicas. El sistema de tipos de lenguaje nos obliga a apoyar la lógica de la vida real. Es fácil de entender con el ejemplo.

Covarianza

Por ejemplo, quieres comprar una flor y tienes dos florería en tu ciudad: la tienda de rosas y la tienda de margaritas.

Si le preguntas a alguien “¿dónde está la florería?” y alguien te dice dónde está la tienda de rosas, ¿estaría bien? sí, porque la rosa es una flor, si quieres comprar una flor puedes comprar una rosa. Lo mismo se aplica si alguien te responde con la dirección de la tienda de margaritas. Este es un ejemplo de covarianza: se le permite emitir A para A, dónde C es una subclase de B, si A produce valores genéricos (devuelve como resultado de la función). La covarianza se trata de productores.

Tipos:

class Flower   
class Rose extends Flower  
class Daisy extends Flower  

interface FlowerShop 
    T getFlower();


class RoseShop implements FlowerShop 
    @Override
    public Rose getFlower() 
        return new Rose();
    


class DaisyShop implements FlowerShop 
    @Override
    public Daisy getFlower() 
        return new Daisy();
    

La pregunta es “¿dónde está la floristería?”, La respuesta es “tienda de rosas allí”:

static FlowerShop tellMeShopAddress() 
    return new RoseShop();

Contravarianza

Por ejemplo, quieres regalar flores a tu novia. Si tu novia ama cualquier flor, ¿puedes considerarla como una persona que ama las rosas o como una persona que ama las margaritas? sí, porque si ama cualquier flor, amaría tanto la rosa como la margarita. Este es un ejemplo del contravarianza: tienes permitido emitir A para A, dónde C es una subclase de B, si A consume valor genérico. La contravarianza se trata de consumidores.

Tipos:

interface PrettyGirl 
    void takeGift(TFavouriteFlower flower);


class AnyFlowerLover implements PrettyGirl 
    @Override
    public void takeGift(Flower flower) 
        System.out.println("I like all flowers!");
    


Estás considerando a tu novia que ama cualquier flor como alguien que ama las rosas, y le estás regalando una rosa:

PrettyGirl girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());

Puedes encontrar más en la Fuente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Respuestas a preguntas comunes sobre programacion y tecnología