Mengganti metode dengan mengirimkan objek subclass di mana supertype diharapkan

12

Saya baru belajar Java, dan saya bukan programmer yang berlatih.

Buku yang saya ikuti mengatakan bahwa ketika mengganti metode, tipe argumen harus sama, tetapi tipe yang dikembalikan dapat kompatibel secara polimorfik.

Pertanyaan saya adalah mengapa argumen tidak dapat diteruskan ke metode overriding bukan tipe subkelas dari tipe super yang diharapkan?

Dalam metode kelebihan beban, metode apa pun yang saya panggil pada objek dijamin akan didefinisikan pada objek.


Catatan tentang duplikat yang disarankan:

The Saran pertama tampaknya menjadi sekitar hirarki kelas dan di mana untuk menempatkan fungsi. Pertanyaan saya lebih fokus pada mengapa pembatasan bahasa ada.

The Saran kedua menjelaskan bagaimana melakukan apa yang saya minta, tapi tidak mengapa hal itu telah dilakukan dengan cara itu. Pertanyaan saya terfokus pada mengapa.

pengguna1720897
sumber
1
ditanya dan dijawab dalam pertanyaan sebelumnya : "Ini umumnya dianggap sebagai gagal Prinsip Pergantian Liskov (LSP), karena kendala tambahan membuat bahwa operasi kelas dasar tidak selalu sesuai untuk kelas turunan ..."
agas
1
kemungkinan duplikat dari Membuat subkelas lebih spesifik dengan
@gnat pertanyaannya bukan apakah memerlukan subtipe yang lebih spesifik untuk argumen melanggar beberapa prinsip komputer, pertanyaannya adalah apakah mungkin, yang dijawab edalorzo.
user949300

Jawaban:

18

Konsep yang awalnya Anda rujuk dalam pertanyaan Anda disebut tipe pengembalian kovarian .

Tipe kembalinya kovarian bekerja karena metode seharusnya mengembalikan objek tipe tertentu dan metode utama sebenarnya dapat mengembalikan subkelasnya. Berdasarkan aturan subtyping dari bahasa seperti Java, jika Smerupakan subtipe T, maka di mana pun Tmuncul kita bisa melewati a S.

Karena itu, aman untuk mengembalikan sebuah Sketika mengganti metode yang diharapkan a T.

Saran Anda untuk menerima bahwa metode penimpaan menggunakan argumen yang merupakan subtipe dari yang diminta oleh metode penimpaan jauh lebih rumit karena mengarah pada ketidakberesan dalam sistem tipe.

Di satu sisi, dengan aturan subtyping yang sama yang disebutkan di atas, kemungkinan besar itu sudah berfungsi untuk apa yang ingin Anda lakukan. Contohnya

interface Hunter {
   public void hunt(Animal animal);
}

Tidak ada yang mencegah implementasi kelas ini dari menerima hewan apa pun, karena sudah memenuhi kriteria dalam pertanyaan Anda.

Tetapi mari kita anggap kita dapat mengganti metode ini seperti yang Anda sarankan:

class MammutHunter implements Hunter {
  @Override
  public void hunt(Mammut animal) {
  }
}

Inilah bagian yang lucu, sekarang Anda bisa melakukan ini:

AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh

Sesuai antarmuka publik AnimalHunterAnda harus dapat berburu binatang apa pun, tetapi sesuai penerapan MammutHunterAnda, Anda hanya menerima Mammutbenda. Oleh karena itu metode overriden tidak memuaskan antarmuka publik. Kami baru saja mematahkan tingkat kesehatan sistem tipe di sini.

Anda dapat menerapkan apa yang Anda inginkan dengan menggunakan obat generik.

interface AnimalHunter<T extends Animal> {
   void hunt(T animal);
}

Maka Anda bisa mendefinisikan MammutHunter Anda

class MammutHunter implements AnimalHunter<Mammut> {
   void hunt(Mammut m){
   }
}

Dan menggunakan kovarians generik dan contravariance, Anda dapat melonggarkan aturan yang menguntungkan Anda saat diperlukan. Misalnya kita bisa memastikan bahwa pemburu mamalia hanya bisa berburu kucing dalam konteks tertentu:

AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());

Seandainya MammalHunteralat AnimalHunter<Mammal>.

Dalam hal ini ini tidak akan diterima:

hunter.hunt(new Mammut()):

Bahkan ketika mamut adalah mamalia, ia tidak akan diterima karena pembatasan pada jenis contravarian yang kami gunakan di sini. Jadi, Anda masih dapat menjalankan beberapa kontrol atas jenis untuk melakukan hal-hal seperti yang Anda sebutkan.

edalorzo
sumber
3

Masalahnya bukan apa yang Anda lakukan dalam metode utama dengan objek yang diberikan sebagai argumen. Masalahnya adalah apa jenis argumen yang menggunakan kode metode Anda diizinkan untuk memberi makan ke metode Anda.

Metode penggantian harus memenuhi kontrak metode yang diganti. Jika metode yang ditimpa menerima argumen dari beberapa kelas dan Anda menimpanya dengan metode yang hanya menerima subkelas, itu tidak memenuhi kontrak. Itu sebabnya tidak valid.

Mengganti metode dengan tipe argumen yang lebih spesifik disebut overloading . Ini mendefinisikan metode dengan nama yang sama tetapi dengan jenis argumen yang berbeda atau lebih spesifik. Metode kelebihan beban tersedia di samping metode asli. Metode mana yang dipanggil tergantung pada jenis yang diketahui pada waktu kompilasi. Untuk membebani metode, Anda harus menghapus penjelasan @Override.

Florian F
sumber