Ko-Kontra-Invarianz u. Liskovsche Substitution.

jono

Top Contributor
Guten Mittag^^,
Unzwar finde ich im Internet zum Thema Ko-Kontra-Invarianz einfach keine hilfreichen Praxisbeispiele. Mal angenommen wir haben ein UML Diagramm mit einer Superklasse und Subklassen, dann sind ja die Subklassen die Varianzen.
Der Typ T‘ des Parameters a einer Methode der Subklasse B ist eine Spezialisierung des Typs T des Parameters der entsprechenden Methode der Superklasse A.(Kovarianz)
A methode(a: T)(Superklasse)
B methode(a: T');(Subklasse)

Der Typ T‘ des Parameters a einer Methode der Superklasse A ist eine Spezialisierung des Typs T des Parameters a der entsprechenden Methode der Subklasse B sein.(Kontravarianz)
A methode(a: T‘)(Superklasse)
B methode(a: T);(Subklasse)

Meine Frage: Kann mir jemand vielleicht mal ein Beispiel geben damit ich das direkt verstehe, die Theorie davon verstehe ich, kann es jedoch in die Praxis nicht ganz umwandeln.
Das damit verbundene Liskovsche Prinzip ist auch so theoretisch in den Vorlesungsfolien formuliert, kann mir das evtl. einer in der Praxis zeigen bzw. mich darauf hinführen?
 

Barista

Top Contributor
Ko- und Kontravarianz haben mit der Spezialisierung durch Bildung von Subklassen wenig zu tun.
Da geht es um Lesen aus einem Container oder Schreiben in einen Container.

Die Parametrisierung durch ein Typ-Argument ist orthogonal(etwas anderes) zur Spezialisierung durch Bildung von Subklassen.

Ein Spezialfall ist die Implemtierung eines generischen Super-Typs:

Java:
class StringList extends List<String> {
   
}

Dann kannst Du die kovariante Spezialisierung wie in Deinem ersten Teil der Frage abbilden:

Java:
interface Container<T> {
    T get();
}

// Methode get mit Typ-Variable implementieren oder Klasse AbstractNumberContainer abstract machen
class AbstractNumberContainer<T extends Number>
implements Container<T> {

    @Override
    public T get() {
        return null;
    }
   
}

class IntegerContainer
extends AbstractNumberContainer<Integer> {

    @Override
    public Integer get() {
        return null;
    }
   
}

Kontravariant klappt das nicht.
 

Barista

Top Contributor
Im Liskovschen Substitutionsprinzip muss ein Parameter einer polymorphen Methode einen allgemeineren oder gleichen Typ wie der entsprechende Parameter der entsprechenden Methode in der Super-Klasse entgegennehmen können.

Dagegen muss der Return-Wert von einem spezielleren oder gleichen Typ sein.

Das klappt aber nur an Instanzen oder Typen von Feldern, Parametern oder Return, weil dort Wildcards erlaubt sind, nicht beim Vererben:
Java:
class A
{
}

class B extends A
{
}

class C extends B
{
}

interface Function<RETURN, PARAM>
{
    RETURN eval(PARAM param);
}

public class GenericsCovariantAndContravariant
{
    /**
     * This method evaluates the specified function with
     * the specified parameter and returns the result.
     *
     * @param <RETURN> return type of Function parameter, covariant
     * @param <PARAM> parameter type of Function parameter, contravariant
     * @param function Function parameter
     */
    static <RETURN, PARAM> RETURN evalFunction(
            Function<? extends RETURN, ? super PARAM> function,
            PARAM param)
    {
        return function.eval(param);
    }

    static void m2() {

        final Function<B, B> function = null; // null is not a problem here, only the behavior of the compiler is important.

        C c = null; // null is not a problem here, only the behavior of the compiler is important.
        A a =
                GenericsCovariantAndContravariant.<B, B>evalFunction(
                        function,
                        c);
    }
}
 

Barista

Top Contributor
Java:
class A
{
}

class B extends A
{
}

class C extends B
{
}

interface Function<RETURN, PARAM>
{
    RETURN eval(PARAM param);
}

public class GenericsCovariantAndContravariant
{
    /**
     * This method evaluates the specified function with
     * the specified parameter and returns the result.
     *
     * @param <RETURN> return type of Function parameter, covariant
     * @param <PARAM> parameter type of Function parameter, contravariant
     * @param function Function parameter
     */
    static <RETURN, PARAM> RETURN evalFunction(
            Function<? extends RETURN, ? super PARAM> function,
            PARAM param)
    {
        return function.eval(param);
    }

    static void m2()
    {
        final Function<B, B> functionBB = null; // null is not a problem here, only the behavior of the compiler is important.

        C c = null; // null is not a problem here, only the behavior of the compiler is important.
        A a =
                GenericsCovariantAndContravariant.<B, B>evalFunction(
                        functionBB,
                        c);

        final Function<C, A> functionCA = null; // null is not a problem here, only the behavior of the compiler is important.

        a =
                GenericsCovariantAndContravariant.<B, B>evalFunction(
                        functionCA,
                        c);
        
        
    }
}
 

Neue Themen


Oben