Pertanyaan Wawancara Umum Java (+ Jawaban)

Artikel ini adalah bagian dari serial: • Pertanyaan Wawancara Koleksi Java

• Pertanyaan Wawancara Sistem Jenis Java

• Pertanyaan Wawancara Konkurensi Java (+ Jawaban)

• Struktur Kelas Java dan Pertanyaan Wawancara Inisialisasi

• Java 8 Pertanyaan Wawancara (+ Jawaban)

• Manajemen Memori dalam Pertanyaan Wawancara Java (+ Jawaban)

• Pertanyaan Wawancara Umum Java (+ Jawaban) (artikel saat ini) • Pertanyaan Wawancara Kontrol Aliran Java (+ Jawaban)

• Pertanyaan Wawancara Pengecualian Jawa (+ Jawaban)

• Pertanyaan Wawancara Anotasi Jawa (+ Jawaban)

• Pertanyaan Wawancara Top Spring Framework

1. Perkenalan

Pada artikel ini, kita akan membahas beberapa contoh pertanyaan dan jawaban wawancara generik Java.

Generik adalah konsep inti di Java, pertama kali diperkenalkan di Java 5. Karena itu, hampir semua basis kode Java akan menggunakannya, hampir menjamin bahwa pengembang akan menemukannya di beberapa titik. Inilah sebabnya mengapa penting untuk memahaminya dengan benar, dan itulah mengapa mereka kemungkinan besar akan ditanyai selama proses wawancara.

2. Pertanyaan

P1. Apa Itu Parameter Jenis Generik?

Type adalah nama kelas atau antarmuka . Seperti yang tersirat dari namanya, parameter tipe generik adalah ketika sebuah tipe dapat digunakan sebagai parameter dalam deklarasi kelas, metode atau antarmuka.

Mari kita mulai dengan contoh sederhana, tanpa obat generik, untuk mendemonstrasikan ini:

public interface Consumer { public void consume(String parameter) }

Dalam kasus ini, tipe parameter metode dari metode konsumsi () adalah String. Itu tidak berparameter dan tidak dapat dikonfigurasi.

Sekarang mari kita ganti tipe String kita dengan tipe generik yang akan kita sebut T. Ini dinamai seperti ini dengan konvensi:

public interface Consumer { public void consume(T parameter) }

Saat kami mengimplementasikan konsumen kami, kami dapat memberikan tipe yang kami inginkan untuk dikonsumsi sebagai argumen. Ini adalah parameter tipe generik:

public class IntegerConsumer implements Consumer { public void consume(Integer parameter) }

Dalam hal ini, sekarang kita dapat menggunakan integer. Kita bisa menukar tipe ini dengan apapun yang kita butuhkan.

P2. Apa Keuntungan Menggunakan Tipe Generik?

Satu keuntungan menggunakan obat generik adalah menghindari gips dan memberikan keamanan tipe. Ini sangat berguna saat bekerja dengan koleksi. Mari kita tunjukkan ini:

List list = new ArrayList(); list.add("foo"); Object o = list.get(0); String foo = (String) o;

Dalam contoh kami, tipe elemen dalam daftar kami tidak dikenal oleh compiler. Artinya satu-satunya yang bisa dijamin adalah bahwa itu adalah sebuah benda. Jadi saat kita mengambil elemen kita, Object adalah apa yang kita dapatkan. Sebagai pembuat kode, kita tahu itu String, tetapi kita harus memasukkan objek kita ke salah satu objek untuk memperbaiki masalah secara eksplisit. Ini menghasilkan banyak kebisingan dan boilerplate.

Selanjutnya, jika kita mulai memikirkan ruangan untuk kesalahan manual, masalah pengecoran menjadi lebih buruk. Bagaimana jika kita secara tidak sengaja memiliki Integer dalam daftar kita?

list.add(1) Object o = list.get(0); String foo = (String) o;

Dalam kasus ini, kita akan mendapatkan ClassCastException saat runtime, karena Integer tidak dapat ditransmisikan ke String.

Sekarang, mari kita coba ulangi sendiri, kali ini menggunakan obat generik:

List list = new ArrayList(); list.add("foo"); String o = list.get(0); // No cast Integer foo = list.get(0); // Compilation error

Seperti yang bisa kita lihat, dengan menggunakan generik kita memiliki pemeriksaan tipe kompilasi yang mencegah ClassCastExceptions dan menghilangkan kebutuhan untuk transmisi.

Keuntungan lainnya adalah untuk menghindari duplikasi kode . Tanpa obat generik, kita harus menyalin dan menempelkan kode yang sama tetapi untuk jenis yang berbeda. Dengan obat generik, kita tidak perlu melakukan ini. Kami bahkan dapat menerapkan algoritme yang berlaku untuk tipe generik.

P3. Apa Itu Type Erasure?

Penting untuk disadari bahwa informasi tipe generik hanya tersedia untuk compiler, bukan JVM. Dengan kata lain , penghapusan tipe berarti bahwa informasi tipe generik tidak tersedia untuk JVM saat runtime, hanya waktu kompilasi .

Alasan di balik pilihan implementasi utama sederhana - menjaga kompatibilitas ke belakang dengan versi Java yang lebih lama. Ketika kode generik dikompilasi menjadi bytecode, ini akan menjadi seolah-olah tipe generik tidak pernah ada. Artinya kompilasi akan:

  1. Ganti tipe generik dengan objek
  2. Ganti jenis terikat (Lebih lanjut tentang ini di pertanyaan nanti) dengan kelas terikat pertama
  3. Masukkan cast yang setara saat mengambil objek generik.

Penting untuk memahami penghapusan tipe. Jika tidak, pengembang mungkin akan bingung dan mengira mereka bisa mendapatkan tipe tersebut pada waktu proses:

public foo(Consumer consumer) { Type type = consumer.getGenericTypeParameter() }

Contoh di atas adalah kode pseudo yang setara dengan apa yang mungkin terlihat tanpa penghapusan tipe, tetapi sayangnya, itu tidak mungkin. Sekali lagi, informasi tipe generik tidak tersedia saat runtime.

P4. Jika Tipe Generik Dihilangkan Saat Membuat Instansiasi Objek, Akankah Kode Masih Dikompilasi?

Karena obat generik tidak ada sebelum Java 5, sangat mungkin untuk tidak menggunakannya sama sekali. Misalnya, obat generik dipasang ke sebagian besar kelas Java standar seperti koleksi. Jika kita melihat daftar kita dari pertanyaan satu, maka kita akan melihat bahwa kita sudah memiliki contoh untuk menghilangkan tipe generik:

List list = new ArrayList();

Meskipun sudah bisa dikompilasi, masih ada kemungkinan akan ada peringatan dari compiler. Ini karena kami kehilangan pemeriksaan waktu kompilasi tambahan yang kami dapatkan dari penggunaan obat generik.

Hal yang perlu diingat adalah bahwa meskipun kompatibilitas mundur dan penghapusan tipe memungkinkan untuk menghilangkan tipe generik, ini adalah praktik yang buruk.

P5. Bagaimana Metode Generik Berbeda dari Tipe Generik?

Metode umum adalah tempat parameter tipe dimasukkan ke metode, yang berada dalam ruang lingkup metode itu. Mari coba ini dengan sebuah contoh:

public static  T returnType(T argument) { return argument; }

Kami telah menggunakan metode statis tetapi bisa juga menggunakan metode non-statis jika kami mau. Dengan memanfaatkan inferensi tipe (dibahas dalam pertanyaan berikutnya), kita bisa memanggil ini seperti metode biasa, tanpa harus menentukan argumen tipe apa pun saat melakukannya.

P6. Apa Itu Jenis Inferensi?

Jenis inferensi adalah saat kompilator dapat melihat tipe argumen metode untuk menyimpulkan tipe generik. Misalnya, jika kita meneruskan T ke metode yang mengembalikan T, maka kompilator bisa mengetahui tipe yang dikembalikan. Mari kita coba ini dengan menggunakan metode umum kita dari pertanyaan sebelumnya:

Integer inferredInteger = returnType(1); String inferredString = returnType("String");

Seperti yang bisa kita lihat, tidak perlu cast, dan tidak perlu memasukkan argumen tipe generik apa pun. Tipe argumen hanya menyimpulkan tipe yang dikembalikan.

P7. Apa itu Parameter Jenis Terikat?

Sejauh ini semua pertanyaan kami telah mencakup argumen tipe generik yang tidak terbatas. Ini berarti argumen tipe umum kita bisa menjadi tipe apa pun yang kita inginkan.

Saat kami menggunakan parameter terbatas, kami membatasi tipe yang dapat digunakan sebagai argumen tipe umum.

Sebagai contoh, katakanlah kita ingin memaksa tipe generik kita selalu menjadi subkelas hewan:

public abstract class Cage { abstract void addAnimal(T animal) }

Dengan menggunakan extends , kami memaksa T menjadi subclass hewan . Kami kemudian dapat memiliki kandang kucing:

Cage catCage;

Tetapi kita tidak dapat memiliki sangkar objek, karena sebuah objek bukanlah subkelas dari hewan:

Cage objectCage; // Compilation error

Satu keuntungan dari ini adalah semua metode animal tersedia untuk penyusun. Kami tahu tipe kami memperluasnya, jadi kami bisa menulis algoritme umum yang bekerja pada hewan apa pun. Ini berarti kami tidak perlu mereproduksi metode kami untuk subkelas hewan yang berbeda:

public void firstAnimalJump() { T animal = animals.get(0); animal.jump(); }

P8. Apakah Mungkin untuk Menyatakan Parameter Jenis Berikat Ganda?

Mendeklarasikan beberapa batasan untuk tipe generik kami adalah mungkin. Dalam contoh kami sebelumnya, kami menentukan satu ikatan, tetapi kami juga dapat menentukan lebih banyak jika kami ingin:

public abstract class Cage

Dalam contoh kita, hewan adalah kelas dan pembandingnya adalah antarmuka. Sekarang, tipe kita harus menghormati kedua batas atas ini. Jika tipe kita adalah subkelas hewan tetapi tidak menerapkan pembanding, maka kode tidak akan dikompilasi. Perlu juga diingat bahwa jika salah satu batas atas adalah kelas, itu harus menjadi argumen pertama.

P9. Apa Itu Jenis Wildcard?

Jenis wildcard merupakan diketahui jenis . Itu diledakkan dengan tanda tanya sebagai berikut:

public static void consumeListOfWildcardType(List list)

Di sini, kami menentukan daftar yang bisa dari jenis apa pun . Kita bisa memasukkan daftar apapun ke dalam metode ini.

P10. Apa itu Wildcard dengan Batas Atas?

Karakter pengganti dengan batas atas adalah jika jenis karakter pengganti diturunkan dari jenis konkret . Ini sangat berguna saat bekerja dengan koleksi dan warisan.

Mari kita coba mendemonstrasikan ini dengan kelas pertanian yang akan menyimpan hewan, pertama tanpa tipe wildcard:

public class Farm { private List animals; public void addAnimals(Collection newAnimals) { animals.addAll(newAnimals); } }

Jika kita memiliki beberapa subclass hewan , seperti kucing dan anjing , kita mungkin membuat asumsi yang salah bahwa kita dapat menambahkan semuanya ke peternakan kita:

farm.addAnimals(cats); // Compilation error farm.addAnimals(dogs); // Compilation error

Ini karena penyusun mengharapkan kumpulan hewan tipe konkret , bukan satu subkelasnya.

Sekarang, mari perkenalkan wildcard batas atas ke metode add animals kita:

public void addAnimals(Collection newAnimals)

Sekarang jika kita coba lagi, kode kita akan dikompilasi. Ini karena kami sekarang memberi tahu kompilator untuk menerima koleksi subtipe hewan apa pun.

P11. Apa Itu Wildcard Tak Terbatas?

Karakter pengganti tak terbatas adalah karakter pengganti tanpa batas atas atau bawah, yang dapat mewakili jenis apa pun.

Penting juga untuk mengetahui bahwa jenis karakter pengganti tidak sama dengan objek. Ini karena karakter pengganti bisa berupa tipe apa pun sedangkan tipe objek secara khusus adalah objek (dan tidak bisa menjadi subkelas objek). Mari kita tunjukkan ini dengan contoh:

List wildcardList = new ArrayList(); List objectList = new ArrayList(); // Compilation error

Sekali lagi, alasan baris kedua tidak dapat dikompilasi adalah karena daftar objek diperlukan, bukan daftar string. Baris pertama dikompilasi karena daftar jenis apa pun yang tidak diketahui dapat diterima.

P12. Apa itu Wildcard dengan Batas Bawah?

Karakter pengganti dengan batas bawah adalah ketika alih-alih memberikan batas atas, kami memberikan batas bawah dengan menggunakan kata kunci super . Dengan kata lain, wildcard dengan batas bawah berarti kita memaksakan tipe menjadi superclass dari tipe terikat kita . Mari coba ini dengan sebuah contoh:

public static void addDogs(List list) { list.add(new Dog("tom")) }

Dengan menggunakan super, kita dapat memanggil addDogs pada daftar objek:

ArrayList objects = new ArrayList(); addDogs(objects);

Ini masuk akal, karena suatu objek adalah kelas super hewan. Jika kita tidak menggunakan wildcard dengan batas bawah, kode tidak akan dikompilasi, karena daftar objek bukanlah daftar hewan.

Jika kita memikirkannya, kita tidak akan dapat menambahkan seekor anjing ke dalam daftar subkelas hewan mana pun, seperti kucing, atau bahkan anjing. Hanya hewan superkelas. Misalnya, ini tidak dapat dikompilasi:

ArrayList objects = new ArrayList(); addDogs(objects);

P13. Kapan Anda Akan Memilih untuk Menggunakan Tipe Berbatas Bawah vs. Tipe Berbatas Atas?

Saat menangani koleksi, aturan umum untuk memilih antara wildcard dengan batas atas atau bawah adalah PECS. PECS adalah singkatan dari producer extends, consumer super.

Ini dapat dengan mudah ditunjukkan melalui penggunaan beberapa antarmuka dan kelas Java standar.

Producer extends hanya berarti jika Anda membuat producer dari tipe generik, maka gunakan kata kunci extends . Mari coba terapkan prinsip ini pada koleksi, untuk melihat mengapa itu masuk akal:

public static void makeLotsOfNoise(List animals) { animals.forEach(Animal::makeNoise); }

Di sini, kami ingin memanggil makeNoise () pada setiap hewan dalam koleksi kami. Ini berarti koleksi kami adalah produsen , karena semua yang kami lakukan dengannya adalah mengembalikan hewan untuk kami operasikan. Jika kita menghilangkan extends , kita tidak akan bisa memasukkan daftar kucing , anjing atau subclass hewan lainnya. Dengan menerapkan prinsip producer extends, kami memiliki fleksibilitas yang paling tinggi.

Konsumen super berarti kebalikan dari meluasnya produsen. Artinya jika kita berurusan dengan sesuatu yang mengkonsumsi elemen, maka kita harus menggunakan kata kunci super . Kami dapat mendemonstrasikan ini dengan mengulangi contoh kami sebelumnya:

public static void addCats(List animals) { animals.add(new Cat()); }

Kami hanya menambahkan ke daftar hewan kami, jadi daftar hewan kami adalah konsumen. Inilah mengapa kami menggunakan kata kunci super . Ini berarti bahwa kita dapat memasukkan daftar kelas super hewan apa pun, tetapi bukan subkelas. Misalnya, jika kami mencoba memasukkan daftar anjing atau kucing, maka kode tidak akan dapat dikompilasi.

Hal terakhir yang perlu dipertimbangkan adalah apa yang harus dilakukan jika suatu koleksi adalah konsumen dan produsen. Contohnya mungkin koleksi di mana elemen ditambahkan dan dihapus. Dalam kasus ini, karakter pengganti tak terbatas harus digunakan.

P14. Adakah Situasi Di Mana Informasi Jenis Generik Tersedia Saat Runtime?

Ada satu situasi di mana tipe generik tersedia saat runtime. Ini adalah saat tipe generik adalah bagian dari tanda tangan kelas seperti:

public class CatCage implements Cage

Dengan menggunakan refleksi, kita mendapatkan parameter tipe ini:

(Class) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0];

Kode ini agak rapuh. Misalnya, ini bergantung pada parameter tipe yang didefinisikan pada superclass langsung. Tapi, ini menunjukkan JVM memang memiliki informasi jenis ini.

Berikutnya » Pertanyaan Wawancara Kontrol Aliran Java (+ Jawaban) « Manajemen Memori Sebelumnya di Pertanyaan Wawancara Java (+ Jawaban)