Apa gunanya operator berlian (<>) di Java 7?

445

Operator berlian di java 7 memungkinkan kode seperti berikut:

List<String> list = new LinkedList<>();

Namun di Java 5/6, saya cukup menulis:

List<String> list = new LinkedList();

Pemahaman saya tentang penghapusan tipe adalah bahwa ini persis sama. (Lagi pula, generik akan dihapus saat runtime).

Kenapa repot-repot dengan berlian sama sekali? Apa fungsi / keamanan jenis baru yang dibolehkan? Jika tidak menghasilkan fungsionalitas baru mengapa mereka menyebutkannya sebagai fitur? Apakah pemahaman saya tentang konsep ini cacat?

tofarr
sumber
4
Perhatikan bahwa ini bukan masalah jika Anda menggunakan metode pabrik statis, karena Java mengetik inferensi pada panggilan metode.
Brian Gordon
ketika Anda menonaktifkan peringatan itu sebenarnya tidak berguna ... :) seperti untuk saya
Renetik
3
Sudah dijawab tetapi juga ada di tutorial Java (sekitar tengah halaman): docs.oracle.com/javase/tutorial/java/generics/…
Andreas Tasoulas
Artikel bagus tentang ini di dZone .
R. Oosterholt
2
Pandangan saya tentang itu adalah bahwa itu adalah gula sintaksis untuk Daftar <String> list = LinkedList baru <Apa pun tipe di sebelah kiri adalah> (); yaitu menjaganya agar tetap generik
Viktor Mellgren

Jawaban:

496

Masalah dengan

List<String> list = new LinkedList();

adalah bahwa di sisi kiri, Anda menggunakan tipe generik diList<String> mana di sisi kanan Anda menggunakan tipe mentahLinkedList . Jenis mentah di Jawa secara efektif hanya ada untuk kompatibilitas dengan kode pra-generik dan tidak boleh digunakan dalam kode baru kecuali Anda benar-benar harus melakukannya.

Sekarang, jika Java memiliki generik dari awal dan tidak memiliki tipe, seperti LinkedList, yang awalnya dibuat sebelum generik, mungkin bisa membuatnya sehingga konstruktor untuk tipe generik secara otomatis menyimpulkan parameter tipenya dari kiri. Sisi-sisi penugasan jika memungkinkan. Tetapi tidak, dan itu harus memperlakukan tipe mentah dan tipe generik berbeda untuk kompatibilitas mundur. Itu membuat mereka perlu membuat cara yang sedikit berbeda , tetapi sama nyamannya, untuk mendeklarasikan instance baru dari objek generik tanpa harus mengulangi parameter tipenya ... operator berlian.

Sejauh contoh asli Anda List<String> list = new LinkedList(), kompiler menghasilkan peringatan untuk tugas itu karena harus. Pertimbangkan ini:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

Generik ada untuk memberikan perlindungan waktu kompilasi terhadap melakukan hal yang salah. Dalam contoh di atas, menggunakan tipe mentah berarti Anda tidak mendapatkan perlindungan ini dan akan mendapatkan kesalahan saat runtime. Inilah sebabnya mengapa Anda tidak harus menggunakan jenis mentah.

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

Namun, operator intan memungkinkan sisi kanan penugasan untuk didefinisikan sebagai instance generik sebenarnya dengan parameter tipe yang sama dengan sisi kiri ... tanpa harus mengetikkan parameter itu lagi. Ini memungkinkan Anda untuk menjaga keamanan obat generik dengan upaya yang hampir sama dengan menggunakan jenis mentah.

Saya pikir hal utama yang perlu dipahami adalah bahwa jenis mentah (tanpa <>) tidak dapat diperlakukan sama dengan jenis generik. Ketika Anda mendeklarasikan tipe mentah, Anda tidak mendapatkan manfaat dan tipe pemeriksaan generik. Anda juga harus ingat bahwa obat generik adalah tujuan umum dari bahasa Jawa ... mereka tidak hanya berlaku untuk konstruktor no-arg dari Collections!

ColinD
sumber
31
Kompatibilitas mundur sangat bagus, tetapi jangan mengorbankan kompleksitas. Mengapa Java 7 tidak bisa hanya memperkenalkan -compatibilitysaklar kompiler sedangkan jika tidak ada maka javacakan melarang semua jenis mentah dan hanya menerapkan jenis generik saja? Ini akan membuat kode kita kurang verbose seperti itu.
Rosdi Kasim
3
@Rosdi: Setuju, tidak perlu untuk jenis mentah dalam kode baru. Namun, saya lebih suka memasukkan nomor versi Java dalam file sumber (alih-alih (salah) menggunakan baris perintah), lihat jawaban saya.
maaartinus
37
Saya pribadi tidak suka penggunaan berlian KECUALI yang Anda tetapkan DAN instantiasi pada baris yang sama. List<String> strings = new List<>()tidak apa-apa, tetapi jika Anda mendefinisikan private List<String> my list;, dan kemudian setengah jalan halaman Anda instantiate my_list = new List<>(), maka itu tidak keren! Apa isi daftar saya lagi? Oh, biarkan aku mencari-cari definisi. Tiba-tiba, manfaat dari jalan pintas berlian berlalu.
rmirabelle
11
@rmirabelle Bagaimana itu berbeda dari: my_list = getTheList()? Ada beberapa cara yang lebih baik bagaimana menangani masalah seperti ini: 1. gunakan IDE yang menunjukkan kepada Anda jenis variabel saat mouse di-arahkan. 2. menggunakan nama-nama variabel yang lebih bermakna, seperti private List<String> strings3. jangan memecah deklarasi dan inisialisasi variabel kecuali Anda benar-benar harus.
Natix
1
@Morfidon: Ya, itu masih berlaku untuk Java 8. Saya cukup yakin Anda harus mendapatkan peringatan.
ColinD
36

Pemahaman Anda sedikit cacat. Operator berlian adalah fitur yang bagus karena Anda tidak perlu mengulangi sendiri. Masuk akal untuk menentukan jenis sekali ketika Anda mendeklarasikan jenis tetapi hanya tidak masuk akal untuk mendefinisikannya lagi di sisi kanan. Prinsip KERING.

Sekarang untuk menjelaskan semua fuzz tentang mendefinisikan tipe. Anda benar bahwa jenis dihapus pada saat runtime tetapi begitu Anda ingin mengambil sesuatu dari Daftar dengan definisi jenis Anda mendapatkannya kembali sebagai jenis yang telah Anda tetapkan saat mendeklarasikan daftar itu jika tidak maka akan kehilangan semua fitur tertentu dan hanya memiliki Fitur objek kecuali ketika Anda akan melemparkan objek yang diambil ke tipe aslinya yang kadang-kadang bisa sangat rumit dan menghasilkan ClassCastException.

Menggunakan List<String> list = new LinkedList()akan memberi Anda peringatan mentah.

Oktavianus A. Damiean
sumber
8
List<String> list = new LinkedList()adalah kode yang benar. Anda tahu ini dan saya juga tahu ini. Dan pertanyaannya (seperti yang saya mengerti) adalah: mengapa hanya java compiler yang tidak mengerti bahwa kode ini cukup aman?
Roman
22
@Roman: List<String> list = new LinkedList()adalah tidak kode yang benar. Tentu, alangkah baiknya jika itu! Dan mungkin bisa saja jika Java memiliki obat generik sejak awal dan tidak harus berurusan dengan kompatibilitas mundur dari jenis generik yang dulunya non-generik, tetapi memang demikian.
ColinD
6
@ColinD Java benar-benar tidak perlu berurusan dengan kompatibilitas ke belakang di setiap baris . Dalam file sumber Java apa pun yang menggunakan generik, tipe non-generik lama harus dilarang (Anda selalu dapat menggunakan <?>jika berinteraksi dengan kode lawas) dan operator berlian yang tidak berguna seharusnya tidak ada.
maaartinus
16

Baris ini menyebabkan peringatan [tidak dicentang]:

List<String> list = new LinkedList();

Jadi, pertanyaannya mengubah: mengapa peringatan [tidak dicentang] tidak ditekan secara otomatis hanya untuk kasus ketika koleksi baru dibuat?

Saya pikir, itu akan menjadi tugas yang jauh lebih sulit daripada menambahkan <>fitur.

UPD : Saya juga berpikir bahwa akan ada kekacauan jika secara legal menggunakan tipe mentah 'hanya untuk beberapa hal'.

Roma
sumber
13

Secara teori, operator intan memungkinkan Anda untuk menulis kode yang lebih ringkas (dan dapat dibaca) dengan menyimpan argumen tipe berulang. Dalam praktiknya, itu hanya dua karakter membingungkan yang lebih memberi Anda apa-apa. Mengapa?

  1. Tidak ada programmer waras yang menggunakan tipe mentah dalam kode baru. Jadi kompilator dapat dengan mudah berasumsi bahwa dengan menulis argumen tanpa tipe Anda ingin menyimpulkannya.
  2. Operator berlian tidak memberikan informasi jenis, hanya mengatakan kompiler, "itu akan baik-baik saja". Jadi, dengan menghilangkannya, Anda tidak bisa membahayakan. Di setiap tempat di mana operator berlian legal, itu bisa "disimpulkan" oleh kompiler.

IMHO, memiliki cara yang jelas dan sederhana untuk menandai sumber sebagai Java 7 akan lebih berguna daripada menciptakan hal-hal aneh seperti itu. Dalam kode yang ditandai, jenis mentah dapat dilarang tanpa kehilangan apa pun.

Btw., Saya tidak berpikir bahwa itu harus dilakukan menggunakan kompilasi switch. Versi Java dari file program adalah atribut dari file tersebut, tidak ada opsi sama sekali. Menggunakan sesuatu yang sepele

package 7 com.example;

dapat memperjelas (Anda dapat memilih sesuatu yang lebih canggih termasuk satu atau lebih kata kunci mewah). Itu bahkan akan memungkinkan untuk mengkompilasi sumber yang ditulis untuk versi Java yang berbeda bersama-sama tanpa masalah. Ini akan memungkinkan pengenalan kata kunci baru (misalnya, "modul") atau menjatuhkan beberapa fitur usang (beberapa kelas non-publik non-bersarang dalam satu file atau apa pun) tanpa kehilangan kompatibilitas apa pun.

maaartinus
sumber
2
Sudahkah Anda mempertimbangkan perbedaan antara new ArrayList(anotherList)dan new ArrayList<>(anotherList)(terutama jika sedang ditugaskan List<String>dan anotherLista List<Integer>)?
Paul Bellora
@ Paul Bellora: Tidak. Anehnya bagi saya, keduanya mengkompilasi. Yang dengan berlian bahkan tidak memberi peringatan. Namun, saya tidak dapat memahami hal ini, dapatkah Anda menguraikannya?
maaartinus
Maaf, saya tidak menjelaskan dengan baik. Lihat perbedaan antara dua contoh ini: ideone.com/uyHagh dan ideone.com/ANkg3T Saya hanya menunjukkan bahwa menggunakan operator berlian bukan masalah jenis mentah, setidaknya ketika argumen dengan batasan generik diajukan di.
Paul Bellora
Sebenarnya saya tidak meluangkan waktu untuk membaca jawaban ColinD - dia mengutip hal yang hampir sama.
Paul Bellora
2
Jadi, jika kita akan memperkenalkan sintaks baru untuk tipe mentah, untuk beberapa tempat yang sangat dibutuhkan, mengapa tidak menggunakan sesuatu seperti new @RawType List(). Itu sudah sintaks Java 8 yang valid dan ketik anotasi memungkinkan untuk menggunakannya di setiap tempat di mana diperlukan, misalnya @RawType List = (@RawType List) genericMethod();. Menimbang bahwa tipe mentah saat ini membuat peringatan kompiler kecuali jika @SuppressWarningstelah ditempatkan, @RawTypeakan menjadi pengganti yang wajar dan sintaksis yang lebih halus tidak diperlukan.
Holger
8

Saat Anda menulis List<String> list = new LinkedList();, kompiler menghasilkan peringatan "tidak dicentang". Anda mungkin mengabaikannya, tetapi jika Anda mengabaikan peringatan ini, Anda mungkin juga kehilangan peringatan yang memberi tahu Anda tentang masalah keamanan tipe nyata.

Jadi, lebih baik menulis kode yang tidak menghasilkan peringatan tambahan, dan operator berlian memungkinkan Anda melakukannya dengan mudah tanpa pengulangan yang tidak perlu.

axtavt
sumber
4

Semua yang dikatakan dalam respons lain valid tetapi kasus penggunaan tidak sepenuhnya IMHO valid. Jika seseorang memeriksa Guava dan terutama koleksi hal-hal terkait, hal yang sama telah dilakukan dengan metode statis. Misalnya Lists.newArrayList () yang memungkinkan Anda untuk menulis

List<String> names = Lists.newArrayList();

atau dengan impor statis

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Guava memiliki fitur-fitur lain yang sangat kuat seperti ini dan saya sebenarnya tidak bisa memikirkan banyak kegunaan untuk <>.

Akan lebih bermanfaat jika mereka menggunakan default perilaku operator berlian, yaitu, tipe disimpulkan dari sisi kiri ekspresi atau jika tipe sisi kiri disimpulkan dari sisi kanan. Yang terakhir inilah yang terjadi di Scala.

allprog
sumber
3

Intinya bagi operator intan adalah untuk mengurangi mengetik kode ketika mendeklarasikan tipe generik. Itu tidak memiliki efek pada runtime sama sekali.

Satu-satunya perbedaan jika Anda menentukan di Java 5 dan 6,

List<String> list = new ArrayList();

adalah bahwa Anda harus menentukan @SuppressWarnings("unchecked")ke list(jika tidak, Anda akan mendapatkan peringatan pemain yang tidak dicentang). Pemahaman saya adalah bahwa operator berlian berusaha membuat pengembangan lebih mudah. Itu tidak ada hubungannya dengan eksekusi runtime generik sama sekali.

Buhake Sindi
sumber
Anda bahkan tidak perlu menggunakan anotasi itu. Setidaknya di Eclipse, Anda bisa memberi tahu kompiler untuk tidak memperingatkan Anda tentang hal ini dan Anda baik-baik saja ...
Xerus
Lebih baik memiliki anotasi. Tidak semua pengembang menggunakan Eclipse di sini.
Buhake Sindi