Kovarian, Invarian, dan Kontravarian dijelaskan dalam bahasa Inggris biasa?

113

Hari ini, saya membaca beberapa artikel tentang Kovarian, Kontravarian (dan Selisih) di Jawa. Saya membaca artikel Wikipedia bahasa Inggris dan Jerman, serta beberapa postingan blog dan artikel lain dari IBM.

Tapi saya masih sedikit bingung tentang apa sebenarnya ini? Beberapa mengatakan ini tentang hubungan antara tipe dan subtipe, beberapa mengatakan ini tentang konversi tipe dan beberapa mengatakan ini digunakan untuk memutuskan apakah suatu metode diganti atau dibebani.

Jadi saya mencari penjelasan yang mudah dalam bahasa Inggris yang sederhana, yang menunjukkan kepada pemula apa itu Kovarian dan Kontravarian (dan Penyimpangan). Poin plus untuk contoh mudah.

tzrm
sumber
Silakan merujuk ke posting ini, ini mungkin berguna untuk Anda: stackoverflow.com/q/2501023/218717
Francisco Alvarado
3
Mungkin lebih baik pertanyaan tipe stack exchange programmer. Jika Anda memposting di sana, pertimbangkan untuk menyatakan apa yang benar-benar Anda pahami, dan apa yang secara khusus membingungkan Anda, karena saat ini Anda meminta seseorang untuk menulis ulang seluruh tutorial untuk Anda.
Hovercraft Full Of Eels

Jawaban:

288

Beberapa mengatakan ini tentang hubungan antara tipe dan subtipe, yang lain mengatakan ini tentang konversi tipe dan yang lain mengatakan ini digunakan untuk memutuskan apakah suatu metode ditimpa atau kelebihan beban.

Semua yang di atas.

Intinya, istilah-istilah ini menjelaskan bagaimana hubungan subtipe dipengaruhi oleh transformasi tipe. Artinya, jika Adan Badalah tipe, fadalah transformasi tipe, dan ≤ relasi subtipe ( A ≤ Bartinya itu Aadalah subtipe dari B), kita punya

  • fadalah kovarian jika A ≤ Bmenyiratkan hal ituf(A) ≤ f(B)
  • fbertentangan jika A ≤ Bmenyiratkan ituf(B) ≤ f(A)
  • f adalah invarian jika tidak satu pun dari yang di atas berlaku

Mari pertimbangkan sebuah contoh. Biarkan f(A) = List<A>dimana Listdideklarasikan oleh

class List<T> { ... } 

Apakah fkovarian, kontravarian, atau invarian? Kovarian akan berarti bahwa a List<String>adalah subtipe dari List<Object>, kontravarian bahwa a List<Object>adalah subtipe dari List<String>dan invarian yang tidak merupakan subtipe dari yang lain, yaitu List<String>dan List<Object>merupakan tipe yang tidak dapat diubah. Di Jawa, yang terakhir benar, kami mengatakan (agak informal) bahwa obat generik tidak berubah.

Contoh lain. Biarkan f(A) = A[]. Apakah fkovarian, kontravarian, atau invarian? Artinya, apakah String [] adalah subtipe dari Objek [], Objek [] adalah subtipe dari String [], atau bukan merupakan subtipe dari yang lain? (Jawaban: Di Jawa, array adalah kovarian)

Ini masih agak abstrak. Untuk membuatnya lebih konkret, mari kita lihat operasi mana di Java yang didefinisikan dalam kaitannya dengan subtipe relasi. Contoh paling sederhana adalah penugasan. Pernyataan

x = y;

akan mengkompilasi hanya jika typeof(y) ≤ typeof(x). Artinya, kami baru saja mempelajari pernyataan itu

ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();

tidak akan dikompilasi di Jawa, tapi

Object[] objects = new String[1];

akan.

Contoh lain di mana hubungan subtipe penting adalah ekspresi pemanggilan metode:

result = method(a);

Secara informal, pernyataan ini dievaluasi dengan menetapkan nilai ake parameter pertama metode, kemudian menjalankan isi metode, dan kemudian menetapkan nilai kembali metode ke result. Seperti tugas sederhana pada contoh terakhir, "sisi kanan" harus merupakan subtipe dari "sisi kiri", yaitu pernyataan ini hanya dapat valid jika typeof(a) ≤ typeof(parameter(method))dan returntype(method) ≤ typeof(result). Artinya, jika metode dideklarasikan oleh:

Number[] method(ArrayList<Number> list) { ... }

tidak ada ekspresi berikut yang akan dikompilasi:

Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());

tapi

Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());

akan.

Contoh lain di mana subtyping penting. Mempertimbangkan:

Super sup = new Sub();
Number n = sup.method(1);

dimana

class Super {
    Number method(Number n) { ... }
}

class Sub extends Super {
    @Override 
    Number method(Number n);
}

Secara informal, runtime akan menulis ulang ini menjadi:

class Super {
    Number method(Number n) {
        if (this instanceof Sub) {
            return ((Sub) this).method(n);  // *
        } else {
            ... 
        }
    }
}

Untuk baris yang ditandai untuk dikompilasi, parameter metode dari metode penggantian harus supertipe dari parameter metode dari metode yang diganti, dan tipe kembalian adalah subtipe dari metode yang diganti. Secara formal, f(A) = parametertype(method asdeclaredin(A))setidaknya harus kontravarian, dan jika f(A) = returntype(method asdeclaredin(A))setidaknya harus kovarian.

Perhatikan "setidaknya" di atas. Itu adalah persyaratan minimum yang akan diberlakukan oleh bahasa pemrograman berorientasi objek aman jenis statis yang masuk akal, tetapi bahasa pemrograman mungkin memilih untuk lebih ketat. Dalam kasus Java 1.4, tipe parameter dan tipe kembalian metode harus identik (kecuali untuk penghapusan tipe) saat mengganti metode, yaitu parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))saat mengganti. Sejak Java 1.5, jenis kembalian kovarian diizinkan saat mengganti, yaitu berikut ini akan dikompilasi di Java 1.5, tetapi tidak di Java 1.4:

class Collection {
    Iterator iterator() { ... }
}

class List extends Collection {
    @Override 
    ListIterator iterator() { ... }
}

Saya harap saya menutupi semuanya - atau lebih tepatnya, menggores permukaan. Tetap saja saya berharap ini akan membantu untuk memahami konsep varian tipe yang abstrak, tetapi penting.

manfaat
sumber
1
Juga, karena tipe argumen kontravarian Java 1.5 diizinkan saat mengganti. Saya pikir Anda melewatkan itu.
Brian Gordon
13
Apakah mereka? Saya baru saja mencobanya di eclipse, dan kompilator mengira saya bermaksud membebani daripada menimpa, dan menolak kode ketika saya menempatkan anotasi @Override pada metode subclass. Apakah Anda memiliki bukti untuk klaim Anda bahwa Java mendukung tipe argumen kontravarian?
meriton
1
Ah, kamu benar. Saya percaya seseorang tanpa memeriksanya sendiri.
Brian Gordon
1
Saya membaca banyak dokumentasi dan menyaksikan beberapa pembicaraan tentang topik ini tetapi sejauh ini penjelasan terbaik. Terima kasih banyak.
minzchickenflavor
1
1 karena benar-benar leman dan sederhana A ≤ B. Notasi itu membuat segala sesuatunya jauh lebih sederhana dan bermakna. Bacaan yang bagus ...
Romeo Sierra
12

Mengambil sistem tipe java, dan kemudian kelas:

Setiap objek dari beberapa tipe T dapat diganti dengan objek subtipe T.

TYPE VARIANCE - METODE KELAS MEMILIKI KONSEKUENSI BERIKUT

class A {
    public S f(U u) { ... }
}

class B extends A {
    @Override
    public T f(V v) { ... }
}

B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;

Dapat dilihat bahwa:

  • T harus merupakan subtipe S ( kovarian, karena B adalah subtipe A ).
  • V harus supertipe dari U ( contravariant , sebagai contra inheritance direction).

Sekarang co- dan contra- berhubungan dengan B menjadi subtipe dari A. Pengetikan yang lebih kuat berikut dapat diperkenalkan dengan pengetahuan yang lebih spesifik. Di subtipe.

Kovarian (tersedia di Java) berguna, untuk mengatakan bahwa seseorang mengembalikan hasil yang lebih spesifik dalam subtipe; terutama terlihat ketika A = T dan B = S. Kontradiksi mengatakan Anda siap untuk menangani argumen yang lebih umum.

Joop Eggen
sumber
8

Varians adalah tentang hubungan antar kelas dengan parameter generik yang berbeda. Hubungan mereka adalah alasan mengapa kami dapat memilih mereka.

Varians Co dan Contra adalah hal yang cukup logis. Sistem tipe bahasa memaksa kita untuk mendukung logika kehidupan nyata. Mudah dipahami dengan contoh.

Kovarian

Misalnya Anda ingin membeli sebuah bunga dan Anda memiliki dua toko bunga di kota Anda: toko mawar dan toko bunga aster.

Jika Anda bertanya kepada seseorang "di mana toko bunga?" dan seseorang memberitahu Anda di mana toko mawar, apakah tidak apa-apa? ya, karena mawar itu bunga, kalau mau beli bunga bisa beli mawar. Hal yang sama berlaku jika seseorang menjawab Anda dengan alamat toko bunga aster. Ini adalah contoh kovarians : Anda diizinkan untuk melakukan cast A<C>ke A<B>, di mana Csubkelasnya B, jikaA menghasilkan nilai-nilai umum (dikembalikan sebagai hasil dari fungsi). Kovarian adalah tentang produsen.

Jenis:

class Flower {  }
class Rose extends Flower { }
class Daisy extends Flower { }

interface FlowerShop<T extends Flower> {
    T getFlower();
}

class RoseShop implements FlowerShop<Rose> {
    @Override
    public Rose getFlower() {
        return new Rose();
    }
}

class DaisyShop implements FlowerShop<Daisy> {
    @Override
    public Daisy getFlower() {
        return new Daisy();
    }
}

Pertanyaannya adalah "dimana toko bunga itu?", Jawabannya "toko mawar disana":

static FlowerShop<? extends Flower> tellMeShopAddress() {
    return new RoseShop();
}

Kontravarian

Misalnya Anda ingin memberikan bunga kepada pacar Anda. Jika pacar Anda menyukai bunga apa pun, dapatkah Anda menganggapnya sebagai orang yang menyukai mawar, atau sebagai orang yang menyukai bunga aster? ya, karena jika dia menyukai bunga apa pun, dia akan menyukai mawar dan bunga aster. Ini adalah contoh kontradiksi : Anda diizinkan untuk melakukan cast A<B>ke A<C>, di mana Csubkelasnya B, jikaA menggunakan nilai generik. Kontradiksi adalah tentang konsumen.

Jenis:

interface PrettyGirl<TFavouriteFlower extends Flower> {
    void takeGift(TFavouriteFlower flower);
}

class AnyFlowerLover implements PrettyGirl<Flower> {
    @Override
    public void takeGift(Flower flower) {
        System.out.println("I like all flowers!");
    }

}

Anda sedang mempertimbangkan pacar Anda yang menyukai bunga apa pun sebagai seseorang yang menyukai mawar, dan memberinya mawar:

PrettyGirl<? super Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());

Anda dapat menemukan lebih banyak di Sumber .

VadzimV
sumber
@ Peter, terima kasih, itu poin yang adil. Invariance adalah ketika tidak ada hubungan antara kelas dengan parameter umum yang berbeda, misalnya Anda tidak dapat mentransmisikan A <B> ke A <C> apapun hubungan antara B dan C.
VadzimV