Pertimbangkan contoh ini (khas dalam buku OOP):
Saya memiliki Animal
kelas, di mana masing Animal
- masing dapat memiliki banyak teman.
Dan subclass seperti Dog
, Duck
, Mouse
dll yang menambah perilaku tertentu seperti bark()
, quack()
dll
Inilah Animal
kelasnya:
public class Animal {
private Map<String,Animal> friends = new HashMap<>();
public void addFriend(String name, Animal animal){
friends.put(name,animal);
}
public Animal callFriend(String name){
return friends.get(name);
}
}
Dan inilah beberapa cuplikan kode dengan banyak pengetikan:
Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());
((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();
Apakah ada cara saya bisa menggunakan obat generik untuk jenis kembali untuk menghilangkan typecasting, sehingga saya bisa mengatakan
jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();
Berikut adalah beberapa kode awal dengan tipe pengembalian yang disampaikan ke metode sebagai parameter yang tidak pernah digunakan.
public<T extends Animal> T callFriend(String name, T unusedTypeObj){
return (T)friends.get(name);
}
Apakah ada cara untuk mengetahui tipe pengembalian saat runtime tanpa menggunakan parameter tambahan instanceof
? Atau setidaknya dengan melewatkan kelas tipe bukan instance dummy.
Saya mengerti obat generik adalah untuk mengkompilasi jenis waktu, tetapi apakah ada solusi untuk ini?
sumber
Tidak. Kompilator tidak tahu tipe apa yang
jerry.callFriend("spike")
akan kembali. Selain itu, implementasi Anda hanya menyembunyikan gips dalam metode tanpa keamanan jenis tambahan. Pertimbangkan ini:Dalam kasus khusus ini, membuat
talk()
metode abstrak dan menimpanya dengan tepat di subkelas akan memberi Anda jauh lebih baik:sumber
Anda bisa menerapkannya seperti ini:
(Ya, ini adalah kode legal; lihat Java Generics: Tipe generik yang didefinisikan sebagai tipe pengembalian saja .)
Jenis kembali akan disimpulkan dari pemanggil. Namun, perhatikan
@SuppressWarnings
anotasi: yang memberi tahu Anda bahwa kode ini tidak aman untuk mengetik . Anda harus memverifikasinya sendiri, atau Anda bisa mendapatkannyaClassCastExceptions
saat runtime.Sayangnya, cara Anda menggunakannya (tanpa menetapkan nilai kembali ke variabel sementara), satu-satunya cara untuk membuat kompiler senang adalah dengan menyebutnya seperti ini:
Meskipun ini mungkin sedikit lebih baik daripada casting, Anda mungkin lebih baik memberikan metode
Animal
abstrak kepada kelastalk()
, seperti yang dikatakan David Schmitt.sumber
jerry.CallFriend<Dog>(...
yang saya pikir terlihat lebih baik.java.util.Collections.emptyList()
diimplementasikan persis seperti ini, dan javadoc-nya mengiklankan dirinya sebagai typesafe.Pertanyaan ini sangat mirip dengan Butir 29 di Jawa Efektif - "Pertimbangkan wadah heterogen yang aman." Jawaban Laz adalah yang paling dekat dengan solusi Bloch. Namun, keduanya put dan get harus menggunakan Class literal untuk keamanan. Tanda tangan itu akan menjadi:
Di dalam kedua metode Anda harus memeriksa bahwa parameternya waras. Lihat Java Efektif dan Kelas javadoc untuk info lebih lanjut.
sumber
Selain itu, Anda dapat meminta metode untuk mengembalikan nilai dalam jenis yang diberikan dengan cara ini
Lebih banyak contoh di sini di dokumentasi Oracle Java
sumber
Ini adalah versi yang lebih sederhana:
Kode sepenuhnya berfungsi:
sumber
Casting to T not needed in this case but it's a good practice to do
. Maksud saya jika diurus dengan benar selama runtime apa artinya "praktik yang baik"?Seperti yang Anda katakan lulus kelas akan baik-baik saja, Anda bisa menulis ini:
Dan kemudian gunakan seperti ini:
Tidak sempurna, tapi ini sejauh yang Anda dapatkan dengan obat generik Java. Ada cara untuk mengimplementasikan Typeafe Heterogenous Containers (THC) menggunakan Token Jenis Super , tetapi itu memiliki masalah sendiri lagi.
sumber
Berdasarkan ide yang sama dengan Token Jenis Super, Anda bisa membuat id yang diketik untuk digunakan sebagai ganti string:
Tapi saya pikir ini mungkin mengalahkan tujuan, karena Anda sekarang perlu membuat objek id baru untuk setiap string dan berpegang pada mereka (atau merekonstruksi mereka dengan informasi jenis yang benar).
Tetapi Anda sekarang dapat menggunakan kelas dengan cara yang Anda inginkan, tanpa gips.
Ini hanya menyembunyikan parameter tipe di dalam id, meskipun itu berarti Anda dapat mengambil tipe dari pengenal nanti jika Anda mau.
Anda perlu menerapkan metode perbandingan dan hashing pada TypedID juga jika Anda ingin dapat membandingkan dua instance id yang identik.
sumber
"Apakah ada cara untuk mengetahui tipe pengembalian saat runtime tanpa parameter tambahan menggunakan instanceof?"
Sebagai solusi alternatif Anda bisa memanfaatkan pola Pengunjung seperti ini. Jadikan Hewan abstrak dan implementasikan Visitable:
Dikunjungi hanya berarti bahwa implementasi Hewan bersedia menerima pengunjung:
Dan implementasi pengunjung dapat mengunjungi semua subclass dari hewan:
Jadi misalnya implementasi Anjing akan terlihat seperti ini:
Kuncinya di sini adalah bahwa karena Anjing tahu jenisnya, ia dapat memicu metode kunjungan berlebih yang relevan dari pengunjung v dengan meneruskan "ini" sebagai parameter. Subclass lain akan menerapkan accept () dengan cara yang persis sama.
Kelas yang ingin memanggil metode khusus subkelas harus mengimplementasikan antarmuka Pengunjung seperti ini:
Saya tahu ini jauh lebih banyak antarmuka dan metode daripada yang Anda tawar-menawar, tetapi ini adalah cara standar untuk menangani setiap subtipe tertentu dengan tepat nol instance of cek dan nol jenis gips. Dan itu semua dilakukan dalam mode agnostik bahasa standar sehingga tidak hanya untuk Java tetapi semua bahasa OO harus bekerja sama.
sumber
Tidak memungkinkan. Bagaimana Peta seharusnya tahu subclass Hewan mana yang akan didapat, hanya diberi kunci String?
Satu-satunya cara ini akan mungkin adalah jika setiap Hewan menerima hanya satu jenis teman (maka itu bisa menjadi parameter dari kelas Hewan), atau metode callFriend () mendapat parameter tipe. Tapi sepertinya Anda kehilangan titik pewarisan: Anda hanya bisa memperlakukan subkelas secara seragam saat menggunakan metode superclass secara eksklusif.
sumber
Saya telah menulis sebuah artikel yang berisi bukti konsep, kelas pendukung, dan kelas tes yang menunjukkan bagaimana Token Jenis Super dapat diambil oleh kelas Anda saat runtime. Singkatnya, ini memungkinkan Anda untuk mendelegasikan ke implementasi alternatif tergantung pada parameter umum aktual yang dilewatkan oleh pemanggil. Contoh:
TimeSeries<Double>
mendelegasikan ke kelas batin pribadi yang menggunakandouble[]
TimeSeries<OHLC>
mendelegasikan ke kelas batin pribadi yang menggunakanArrayList<OHLC>
Lihat: Menggunakan TypeTokens untuk mengambil parameter umum
Terima kasih
Richard Gomes - Blog
sumber
Ada banyak jawaban yang bagus di sini, tetapi ini adalah pendekatan yang saya ambil untuk tes Appium di mana bekerja pada satu elemen dapat menghasilkan kondisi aplikasi yang berbeda berdasarkan pengaturan pengguna. Meskipun tidak mengikuti konvensi contoh OP, saya harap ini membantu seseorang.
Jika Anda tidak ingin melempar kesalahan, Anda dapat menangkapnya seperti:
sumber
Tidak juga, karena seperti yang Anda katakan, kompiler hanya tahu bahwa callFriend () mengembalikan Hewan, bukan Anjing atau Bebek.
Tidak bisakah Anda menambahkan metode abstrak makeNoise () ke Animal yang akan diimplementasikan sebagai kulit kayu atau dukun oleh subkelasnya?
sumber
Apa yang Anda cari di sini adalah abstraksi. Kode terhadap antarmuka lebih banyak dan Anda harus melakukan casting lebih sedikit.
Contoh di bawah ini dalam C # tetapi konsepnya tetap sama.
sumber
Saya melakukan hal berikut di lib kontraktor saya:
subclassing:
setidaknya ini berfungsi di dalam kelas saat ini dan ketika memiliki referensi yang diketik kuat. Beragam warisan bekerja, tetapi menjadi sangat rumit :)
sumber
Saya tahu ini adalah hal yang sama sekali berbeda dari yang diminta. Cara lain untuk menyelesaikan ini adalah refleksi. Maksud saya, ini tidak mengambil keuntungan dari Generics, tetapi ini memungkinkan Anda meniru, dalam beberapa hal, perilaku yang ingin Anda lakukan (membuat anjing menggonggong, membuat dukun bebek, dll.) Tanpa mengurus jenis casting:
sumber
bagaimana dengan
}
sumber
Ada pendekatan lain, Anda bisa mempersempit tipe kembali ketika Anda mengganti metode. Di setiap subclass Anda harus mengganti callFriend untuk mengembalikan subclass itu. Biayanya adalah beberapa deklarasi callFriend, tetapi Anda dapat mengisolasi bagian-bagian umum ke metode yang disebut secara internal. Ini tampaknya jauh lebih sederhana bagi saya daripada solusi yang disebutkan di atas, dan tidak perlu argumen tambahan untuk menentukan tipe pengembalian.
sumber
public int getValue(String name){}
tidak bisa dibedakan daripublic boolean getValue(String name){}
sudut pandang kompiler. Anda perlu mengubah tipe parameter atau menambah / menghapus parameter agar kelebihan beban dapat dikenali. Mungkin aku hanya salah paham padamu ...Anda dapat mengembalikan jenis apa pun dan menerima langsung suka. Tidak perlu mengetik typecast.
Ini yang terbaik jika Anda ingin menyesuaikan jenis catatan lain daripada kursor nyata.
sumber
(Cursor) cursor
misalnya.cursor
harus berupaCursor
, dan akan selalu mengembalikanPerson
(atau nol). Menggunakan obat generik menghilangkan pengecekan kendala ini, membuat kode tidak aman.