Berikan contoh fungsi yang mendemonstrasikan kovariansi dan kontravarian dalam kasus overloading dan overriding di Java? [Tutup]

Jawaban:

155

Kovarian:

class Super {
  Object getSomething(){}
}
class Sub extends Super {
  String getSomething() {}
}

Sub # getSomething adalah kovarian karena mengembalikan subclass dari tipe kembalian Super # getSomething (tetapi memenuhi kontrak Super.getSomething ())

Kontravarian

class Super{
  void doSomething(String parameter)
}
class Sub extends Super{
  void doSomething(Object parameter)
}

Sub # doSomething adalah contravariant karena mengambil parameter superclass dari parameter Super # doSomething (tapi, sekali lagi, memenuhi kontrak Super # doSomething)

Perhatikan: contoh ini tidak berfungsi di Java. Compiler Java akan membebani dan tidak mengganti metode doSomething () -. Bahasa lain mendukung gaya kontravarian ini.

Generik

Ini juga mungkin untuk Generik:

List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;

Anda sekarang dapat mengakses semua metode covariantListyang tidak mengambil parameter umum (karena harus berupa sesuatu yang "memperluas Objek"), tetapi getter akan berfungsi dengan baik (karena objek yang dikembalikan akan selalu berjenis "Objek")

Hal yang sebaliknya berlaku untuk contravariantList: Anda dapat mengakses semua metode dengan parameter generik (Anda tahu itu pasti kelas super dari "String", sehingga Anda selalu dapat meneruskan satu), tetapi tidak ada getter (Jenis yang dikembalikan mungkin dari supertipe String lainnya )

Hardcode
sumber
79
Contoh kontravarian pertama tidak berfungsi di Jawa. doSomething () di kelas Sub adalah overload, bukan override.
Craig P. Motlin
15
Memang. Java tidak mendukung argumen kontravarian dalam subtipe. Hanya kovarians untuk jenis pengembalian metode perhatian apa (seperti pada contoh pertama).
the_dark_destructor
Jawaban yang bagus. Kovarian terlihat logis bagi saya. Tapi bisakah Anda menunjukkan sebuah paragraf di JLS yang menjelaskan pelanggaran? Mengapa Sub.doSomething dipanggil?
Mikhail
2
Seperti yang ditunjukkan Craig, sebenarnya tidak. Saya pikir di sini adalah bentrokan antara overriding dan overloading dan SUN memang memilih (seperti biasa) opsi yang kompatibel ke belakang. Jadi di Java Anda tidak bisa menggunakan parameter kontravarian saat mengganti metode.
Hardcode
1
Anda akan senang mengetahui mengapa saya mendapatkan suara negatif untuk jawaban saya.
Hardcode
48

Co-variance: Iterable dan Iterator. Hampir selalu masuk akal untuk mendefinisikan co-varian Iterableatau Iterator. Iterator<? extends T>dapat digunakan sama seperti Iterator<T>- satu-satunya tempat di mana parameter tipe muncul adalah tipe kembalian dari nextmetode, sehingga dapat dengan aman di-up-cast T. Tetapi jika Anda memiliki Sperluasan T, Anda juga dapat menetapkan Iterator<S>ke tipe variabel Iterator<? extends T>. Misalnya jika Anda mendefinisikan metode find:

boolean find(Iterable<Object> where, Object what)

Anda tidak akan dapat memanggilnya dengan List<Integer>dan 5, jadi lebih baik didefinisikan sebagai

boolean find(Iterable<?> where, Object what)

Kontra-varian: Pembanding. Hampir selalu masuk akal untuk digunakan Comparator<? super T>, karena dapat digunakan sama seperti Comparator<T>. Parameter tipe muncul hanya sebagai comparetipe parameter metode, sehingga Tdapat diteruskan dengan aman. Misalnya jika Anda memiliki DateComparator implements Comparator<java.util.Date> { ... }dan ingin mengurutkan List<java.sql.Date>dengan pembanding itu ( java.sql.Dateadalah sub-kelas dari java.util.Date), Anda dapat melakukannya dengan:

<T> void sort(List<T> what, Comparator<? super T> how)

tapi tidak dengan

<T> void sort(List<T> what, Comparator<T> how)
Yardena
sumber
-4

Lihatlah prinsip substitusi Liskov . Akibatnya, jika kelas B memperluas kelas A maka Anda harus dapat menggunakan B setiap kali A diperlukan.

extraneon
sumber
6
Ini tidak menjawab pertanyaan dan menyesatkan. Sangat mungkin untuk merancang sistem varian yang mematahkan ketepatan semantik dan karena itu melanggar LSP.
Matt Whipple
ini bukan kasus untuk contra variantdikatakan. super.doSomething("String")tidak bisa digantikan oleh sub.doSomething(Object).
zinking
Ini bukan pertanyaannya
OlivierTerrien