Covariance and contravariance (computer science)

In object- oriented programming, covariance and contravariance means that if one aspect (ie a type declaration ) similar to the inheritance direction ( covariant ) or opposite to this ( contravariant ) is. Located in the subclass does not change compared to the luxury class before, this is called invariance.

The terms are subject to the considerations of the Ersetzbarkeitsprinzips: Objects of the upper class must be replaced by objects of its subclasses. This means for example that the methods of the subclass must accept at least the parameters that would also accept the superclass ( contravariance ). The methods of the subclass must also return values ​​that are consistent with the upper class, so are never more general type as the return type of the superclass ( covariance ).

Term origin

The terms contravariance and covariance are derived in the object orientation from getting that behave the types of the parameters considered with the inheritance hierarchy of substitution ( covariant ) or opposite to the inheritance hierarchy ( contravariant ).

Occurrence of variances

You can choose between co-, counter- and invariance in

  • Methods Argument types ( the types of the parameters passed )
  • Result types ( the types of the return value )
  • Other signature extensions (eg, exception types in the throws clause in Java)

Differ.

By the substitution principle, the following performance opportunities for variances arising in the inheritance hierarchy of object-oriented programming:

Covariance, contravariance and invariance

Covariance means that the type hierarchy with the inheritance hierarchy of the classes has to be considered the same direction. So if you want to customize an inherited method, the adjustment is covariant if the type of a method parameter in the upper class is a generic type of the parameter type of this method in the subclass.

If the type hierarchy opposite approaches the inheritance hierarchy of classes to be considered, it is called contravariance. If the types can not be changed in the upper and lower class, it is called invariance.

In the object-oriented modeling, it is often desirable that the input parameters of methods are covariant. This, however, the substitution principle is violated. Overcharging is handled differently in this case, by the various programming languages.

  • Covariance, contravariance and invariance at a glance

Type hierarchy of the method parameter is contrary to the inheritance hierarchy of ClassA and ClassB

Type hierarchy of the return value of the method is with the inheritance hierarchy of ClassA and ClassB

Type hierarchy of the method parameter remains unchanged

Example in terms of program code

The basic principle in programming languages ​​such as C , Java and C # that variables and parameters are contravariant, while methods returns are covariant:

Private class Giraffe   {      public Giraffe ( string name)      {         Name = name;      }      public string Name {get; private set; }   }     public string GetNameFromAnimal ( Animal animal)   {      return animal.Name;   }     [ Test]   public void contravariance ()   {       var herby = new Giraffe ( " Herby ");       / / Contravariant transformation of Giraffe by Animal       var name = GetNameFromAnimal ( herby );       Assert.AreEqual ( " Herby ", name);   }   private class Giraffe   {      public Giraffe ( string name)      {         Name = name;      }      public string Name {get; private set; }   }     public string GetNameFromGiraffe ( giraffe animal)   {      return animal.Name;   }     [ Test]   public void Covariance ()   {       var herby = new Giraffe ( " Herby ");       / / Covariant transformation of the return value of String to Object       objectName = GetNameFromGiraffe ( herby );       Assert.AreEqual ( (object) " Herby ", name);   }   private class Giraffe   {      public Giraffe ( string name)      {         Name = name;      }      public string Name {get; private set; }   }     public string GetNameFromGiraffe ( giraffe animal)   {      return animal.Name;   }     [ Test]   public void Invariance ()   {       var herby = new Giraffe ( " Herby ");       / / No conversion of data types       string name = GetNameFromGiraffe ( herby );       Assert.AreEqual ( " Herby ", name);   } Example using illustrations

The following clarifies when type safety is guaranteed, if you want to replace a function by another. This can be then transferred to methods in object-orientation in addition, if after the Liskovschen substitution principle methods are replaced by objects.

Be and functions, for example, have the following signature:

Where and, and

, Where and.

As you can see, is a superset of, but a subset of. If the function is used instead of, then it is called the input type C contravariant, covariant the output type D. In the example, the replacement can be done without injury type, since the input of covering the entire range of input. In addition, results that do not exceed the value range of supplies.

Correctness of contra - and covariance

As a model, the UML notation is used to represent the inheritance hierarchy:

Contravariance covariance invariance   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐   │ T │ │ │ │ ClassA ClassA ClassA │ │ │   ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤   │ │ │ │ │ │ │ │   ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤   │ │ │ method (t ': T') │ │ method (): T │ │ method (t: T & ) │   └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘        ↑ ↑ ↑ ↑   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐   │ T ' │ │ │ │ ClassB ClassB ClassB │ │ │   ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤   │ │ │ │ │ │ │ │   ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤   │ │ │ method (t: T) │ │ method (): T ' │ │ method (t: T & ) │   └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ Contravariance: The substitution principle is respected, because you can method (t: T) using the subclass ClassB as it would be the method of upper class ClassA. Check: You can be the method (t: T) is a variable of a more specific type T 'is passed as a result of heredity T' contains all the information that are also in T.

Covariance: The substitution principle is respected, because you can method (): use as it would be the method of upper class ClassA T ' subclass ClassB. Check: The return value of the method of ClassB is T '. We must pass this value to a declared variable of type T, since T ' features due to the inheritance of all the information that are also in T.

Type safety methods

Due to the properties of the substitution principle static type safety is guaranteed, if the argument types contravariant and the result types are covariant.

Type-unsafe covariance

The in the object-oriented modeling often desirable covariance of the method parameter is supported, despite uncertainty resulting type in many programming languages.

An example of the type of uncertainty covariant method parameter is found in the following classes person and physician, and their specializations child and pediatrician. The method of parameter examine in class pediatrician is a specialization of the parameter the same method of physician and therefore covariant.

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ T │ │ │ ClassA ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ method (t: T) │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘       ↑ ↑ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ T ' │ │ │ ClassB ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ method (t ': T') │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ Example for type-unsafe covariance ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ │ person physician │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ hold still () │ │ examine (p: Person ) │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘           ↑ ↑ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ │ child pediatrician │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ TapferSein │ () │ │ examine (k: Kind ) │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ In Java code:

Public class Person {          protected String name; public String getName () {return name; }          public Person ( final String s) { name = n; }          public void hold still () {              System.out.println (name " stands still ");          }      }        public class Child extends Person {          brave boolean = false;          public Child ( final String s) { super (s ); }          public void hold still () {              if ( Gallant)                  System.out.println (name " stands still ");              else                  System.out.println (name " says AUA and defends itself ");          }          public void tapferSein () {              brave = true;              System.out.println (name " is brave ");          }      }        public class Doctor extends Person {          public doctor ( final String s) { super (s ); }          public void examine ( Person person) {              System.out.println (name " investigated " person.getName ());              person.stillHalten ();          }      }        public class extends pediatrician doctor {          public Pediatrician ( final String s) { super (s ); }          public void examine ( Child child ) {              System.out.println (name "examines child " kind.getName ());              kind.tapferSein ();              kind.stillHalten ();          }      } A program using the classes might look like this:   public class Main {       public static void main ( String [ ] args ) {          Doctor doctor = new pediatrician ( " Dr. Meier ");          Person person = new Person ( "Mrs. Smith" );          arzt.untersuche (person);          Child child = new Child ( "Little Susie" );          arzt.untersuche ( child );          / / And now CORRECTLY          Pediatricians = new pediatrician ( " Dr. Jackson");          kinderarzt.untersuche (person);          kinderarzt.untersuche ( child );       }   } The output is then:

Dr. Meier examines Mrs. Muller Mrs. Muller keeps silent Dr. Meier examines small Susi small Susi says AUA and defends itself Dr. Schulze examined Mrs. Muller Mrs. Muller keeps silent Dr. Schulze examines child small Susi small Susi is brave Little Susie stands still It is important that the object must be declared doctor right because here not overwrite a method, but is overloaded, and the process of overloading is bound to the static type of the object. The result can be seen in the comparison of expenditures: Dr. Meier can not investigate children, Dr. Schulze does.

In Java, the example still works: The method of investigating doctor is not overridden in a pediatrician but merely overloaded due to the different parameters. According to the language definition of a Java method must be what you want to overwrite, have the same signature ( consisting of the parameter any exceptions in Java).

Covariance on arrays

For array data types covariance can lead to a problem in languages ​​like C , Java and C #, as they maintained internally the data type after the conversion:

@ Test ( expected = ArrayStoreException.class )   public void ArrayCovariance ()   {       Giraffe [ ] = new Giraffe giraffes;       Snake alice = new Snake ( "Alice ");         / / Covariance (typecast in inheritance direction)       Animal [ ] animals = giraffes;         / / Results in the runtime an exception, because the array is internally of type Giraffe       animals = alice;   }   [Test, ExpectedException (typeof ( ArrayTypeMismatchException ) ) ]   public void ArrayCovariance ()   {       var = new Giraffe giraffes;       var alice = new Snake ( "Alice ");         / / Covariance       Animal [ ] animals = giraffes;         / / Exception at run time       animals = alice;   } This can be remedied using generic data types. In particular, in C #, the interface IEnumerable, which is implemented by the array data type, are used to prevent writes:

[ Test]   public void ArrayCovariance ()   {       var = new Giraffe giraffes;       var alice = new Snake ( "Alice ");         IEnumerable animals = giraffes;      . tiere.ToList () Add ( alice );         Assert.Contains ( alice, animals );   } see also

  • Overwrite ( object oriented programming)
  • Generic programming in Java 5.0 # variance cases
  • Object-Oriented Programming
415492
de