Bazıları bunun türler ve alt türler arasındaki ilişkiyle ilgili olduğunu söyler, diğerleri bunun tür dönüştürme ile ilgili olduğunu söyler ve diğerleri bir yöntemin üzerine mi yoksa aşırı mı yükleneceğine karar vermek için kullanıldığını söyler.
Yukarıdakilerin hepsi.
Özünde, bu terimler alt tür ilişkisinin tür dönüşümlerinden nasıl etkilendiğini açıklar. Yani, eğer A
ve B
türlerse, f
bir tür dönüşümü ve sub alt tip ilişkisi (yani , bir alt türü olduğu A ≤ B
anlamına gelir ), bizdeA
B
f
kovaryanttır eğer A ≤ B
ima ediyorsaf(A) ≤ f(B)
f
aykırıdır, eğer bunu A ≤ B
ima edersef(B) ≤ f(A)
f
Yukarıdakilerin hiçbiri geçerli değilse değişmezdir
Bir örnek ele alalım. Let f(A) = List<A>
nereye List
kadar beyan
class List<T> { ... }
f
Eşdeğişken mi, aykırı mı yoksa değişmez mi? Kovaryant bir anlamına geleceğini List<String>
bir alt tipi olan List<Object>
bir olduğunu, kontravaryant List<Object>
bir alt tipi olan List<String>
, ne diğer bir alt tipi olduğu ve değişmez ie List<String>
ve List<Object>
değiştirilemez türleridir. Java'da ikincisi doğrudur, biz (biraz gayri resmi olarak) jeneriklerin değişmez .
Başka bir örnek. Let f(A) = A[]
. Dır-dirf
Eşdeğişken mi, aykırı yoksa değişmez mi? Yani, String [] bir Object [] alt türü, Object [] bir String [] alt türü mü, yoksa ikisi de diğerinin bir alt türü değil mi? (Cevap: Java'da diziler eş değişkendir)
Bu hala oldukça soyuttu. Daha somut hale getirmek için, Java'da hangi işlemlerin alt tip ilişkisi açısından tanımlandığına bakalım. En basit örnek atamadır. İfade
x = y;
yalnızca eğer derlenir typeof(y) ≤ typeof(x)
. Yani, ifadelerin
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
Java'da derlenmeyecek, ancak
Object[] objects = new String[1];
niyet.
Alt tür ilişkisinin önemli olduğu başka bir örnek, bir yöntem çağırma ifadesidir:
result = method(a);
Gayri resmi konuşursak, bu ifade, değerinin a
yöntemin ilk parametresine atanması, ardından yöntemin gövdesinin çalıştırılması ve ardından yöntemlerin dönüş değerinin atanması ile değerlendirilir result
. Son örnekteki düz atama gibi, "sağ taraf", "sol tarafın" bir alt türü olmalıdır, yani bu ifade yalnızca typeof(a) ≤ typeof(parameter(method))
ve ise geçerli olabilir returntype(method) ≤ typeof(result)
. Yani, yöntem şu şekilde bildirilirse:
Number[] method(ArrayList<Number> list) { ... }
aşağıdaki ifadelerin hiçbiri derlenmez:
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
fakat
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
niyet.
Alt tiplemenin önemli olduğu başka bir örnek. Düşünmek:
Super sup = new Sub();
Number n = sup.method(1);
nerede
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
Gayri resmi olarak, çalışma zamanı bunu şu şekilde yeniden yazar:
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
İşaretli satırın derlenmesi için, geçersiz kılınan yöntemin yöntem parametresinin, geçersiz kılınan yöntemin yöntem parametresinin bir üst türü olması ve dönüş türünün, geçersiz kılınan yöntemin bir alt türü olması gerekir. Resmi olarak konuşursak, f(A) = parametertype(method asdeclaredin(A))
en azından aykırı f(A) = returntype(method asdeclaredin(A))
olmalı ve en azından ortak değişken olmalıdır.
Yukarıdaki "en az" a dikkat edin. Bunlar, herhangi bir makul statik tip güvenli nesne yönelimli programlama dilinin uygulayacağı minimum gereksinimlerdir, ancak bir programlama dili daha katı olmayı seçebilir. Java 1.4 durumunda, metotları geçersiz kılarken, yani geçersiz kılarken, parametre türleri ve yöntem dönüş türleri aynı olmalıdır (tür silme hariç) parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))
. Java 1.5'ten beri, geçersiz kılma sırasında kovaryant dönüş türlerine izin verilir, yani aşağıdakiler Java 1.5'te derlenir, ancak Java 1.4'te derlenmez:
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
Umarım her şeyi kapatmışımdır - ya da daha doğrusu yüzeyi çizmişimdir. Yine de, soyut, ancak önemli tür varyansı kavramını anlamaya yardımcı olacağını umuyorum.