Niektórzy uważają, że jest o relationshop pomiędzy typów i podtypów, inne znaczy chodzi o rodzaj konwersji i inni twierdzą, że jest wykorzystywany do określenia, czy metoda jest zastąpione lub przeciążenia.
Wszystkie powyższe.
W istocie te warunki opisują, w jaki sposób transformacja typu wpływa na relację podtypu. Oznacza to, że jeśli A
i B
są typy, f
jest transformacja typu i ≤ relacji podtypu (tj A ≤ B
oznacza, że A
jest podtypem B
), mamy
f
jest kowariantna jeśli A ≤ B
oznacza, że f(A) ≤ f(B)
f
jest kontrawariantny jeśli A ≤ B
oznacza, że f(B) ≤ f(A)
f
jest niezmienna, jeśli żadna z powyższych posiada
Rozważmy przykład. Niech f(A) = List<A>
gdzie List
deklaruje
class List<T> { ... }
Czy f kowariantna, kontrawariantny lub niezmiennego? Kowariantna oznaczałoby, że List<String>
jest podtypem List<Object>
, kontrawariantny że List<Object>
jest podtypem List<String>
i niezmienna, że nie jest podtypem z drugiej strony, to znaczy List<String>
i List<Object>
są niewymienialne typy. W języku Java, ta ostatnia jest prawdziwa, mówimy (nieco nieformalnie), że generics są niezmienne.
Inny przykład. Niech f (A) = A[]
. Czy f jest kowariantny, sprzeczny lub niezmienny? Oznacza to, że String [] jest podtypem Object [], Object [] podtypu String [], czy też nie jest podtypem drugiego? (Odpowiedź: W Javie tablice są kowariantne)
To wciąż było dość abstrakcyjne. Aby było to bardziej konkretne, przyjrzyjmy się, które operacje w Javie są zdefiniowane pod kątem relacji podtypu. Najprostszym przykładem jest przydział. Oświadczenie
x = y;
będzie skompilować tylko jeśli typeof (y) ≤ typeof (x).Oznacza to, że właśnie dowiedział się, że oświadczenia
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
nie będzie kompilować w Javie, ale
Object[] objects = new String[1];
wola.
Innym przykładem, gdzie liczy się relacja podtyp jest wyrażeniem wywołania metody:
result = method(a);
Nieformalnie mówiąc, to stwierdzenie jest oceniany przez przypisanie wartości a
do sposobu pierwszego parametru, a następnie wykonanie ciało metody , a następnie przypisanie metod zwraca wartość do result
. Podobnie jak zwykłe przypisanie w ostatnim przykładzie, "prawa strona" musi być podtypem "lewej strony", tj. Ta instrukcja może być ważna tylko wtedy, gdy typeof (a) ≤ typeof (parametr (metoda)) i returntype (metoda) ≤ typeof (wynik). Oznacza to, że jeśli metoda jest zadeklarowana przez:
Number[] method(ArrayList<Number> list) { ... }
żaden z poniższych wyrażeń skompiluje:
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
ale
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
wola.
Kolejny przykład, w którym podrzędność ma znaczenie. Rozważmy:
Super sup = new Sub();
Number n = sup.method(1);
gdzie
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
Nieformalnie, środowisko wykonawcze będzie przepisać to:
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
Dla zaznaczonej linii skompilować, parametr metoda metody nadrzędnego musi być supertype parametru metody zastąpionej metody, a typ zwracany - podtypu metody nadpisanej. Formalnie f (A) = parametr typu (metoda asdeclaredin (A)) musi być przynajmniej kontrowersyjna, a jeśli f (A) = returntype (metoda asdeclaredin (A)) musi przynajmniej być kowariantna.
Uwaga "przynajmniej" powyżej. Są to minimalne wymagania, które wymusi każdy rozsądny statycznie bezpieczny język programowania obiektowego, ale język programowania może być bardziej restrykcyjny. W przypadku języka Java 1.4 typy parametrów i metody zwracania typów muszą być identyczne (z wyjątkiem wymazywania typu), gdy nadpisywane są metody, tj. Parametr typu (metoda asdeclaredin (A)) = parametr typu (metoda asdeclaredin (B)) podczas przesłonięcia. Ponieważ Java 1.5, kowariantna typy zwracane są dozwolone, gdy nadrzędne, czyli dodaje się skompilować w Java 1.5, ale nie w Java 1.4:
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
Mam nadzieję, że wszystko pokryte - czy raczej powierzchownie. Nadal mam nadzieję, że pomoże to zrozumieć abstrakcyjną, ale ważną koncepcję wariancji typu.
Proszę odnieść się do tego postu, może to być pomocne dla Ciebie: http://stackoverflow.com/q/2501023/218717 –
Może lepiej stos typu wymiana pytanie programisty. Jeśli publikujesz tam, zastanów się nad tym, co rozumiesz i co cię szczególnie denerwuje, ponieważ teraz prosisz kogoś o ponowne napisanie całego samouczka. –