Mengapa menggunakan kartu liar dengan pernyataan impor Java buruk?

419

Jauh lebih nyaman dan bersih untuk menggunakan satu pernyataan seperti

import java.awt.*;

daripada mengimpor sekelompok kelas individu

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...

Apa yang salah dengan menggunakan wildcard dalam importpernyataan itu?

jnancheta
sumber

Jawaban:

518

Satu-satunya masalah dengan itu adalah bahwa itu mengacaukan namespace lokal Anda. Sebagai contoh, katakanlah Anda sedang menulis aplikasi Swing, dan perlu java.awt.Event, dan juga berinteraksi dengan sistem kalender perusahaan, yang memiliki com.mycompany.calendar.Event. Jika Anda mengimpor keduanya menggunakan metode wildcard, salah satu dari tiga hal ini terjadi:

  1. Anda memiliki konflik penamaan langsung antara java.awt.Eventdan com.mycompany.calendar.Event, sehingga Anda bahkan tidak dapat mengompilasi.
  2. Anda benar-benar hanya dapat mengimpor satu (hanya satu dari dua impor Anda yang melakukannya .*), tetapi yang salah, dan Anda berjuang untuk mencari tahu mengapa kode Anda mengklaim jenis itu salah.
  3. Ketika Anda mengkompilasi kode Anda tidak ada com.mycompany.calendar.Event, tetapi ketika mereka nanti menambahkan satu kode Anda yang sebelumnya valid tiba-tiba berhenti mengkompilasi.

Keuntungan dari mendaftar secara eksplisit semua impor adalah bahwa saya dapat melihat sekilas kelas mana yang ingin Anda gunakan, yang hanya membuat membaca kode lebih mudah. Jika Anda hanya melakukan satu hal cepat, tidak ada yang salah secara eksplisit , tetapi pengelola masa depan akan berterima kasih atas kejelasan Anda sebaliknya.

Benjamin Pollack
sumber
7
Ini adalah skenario pertama yang akan terjadi. Kompilator memperhatikan bahwa ada dua kelas Peristiwa dan memberikan kesalahan.
jan.vdbergh
38
Pastikan untuk memeriksa komentar saya di bawah ini - ada masalah yang lebih besar dengan jenis yang ditambahkan ke lib pihak ketiga seiring waktu. Anda dapat memiliki kode kompilasi yang berhenti dikompilasi setelah seseorang menambahkan jenis ke stoples yang Anda andalkan.
Scott Stanchfield
6
mengenai masalah 1: secara teknis, Anda dapat mengkompilasi, tetapi Anda harus menggunakan nama kelas yang sepenuhnya memenuhi syarat setiap kali.
Kip
1
Anda dapat menyelesaikan konflik semacam ini tanpa membuat daftar setiap kelas secara eksplisit, yang menyebabkan masalah tersendiri.
rpjohnst
196

Ini suara untuk impor bintang. Pernyataan impor dimaksudkan untuk mengimpor paket , bukan kelas. Jauh lebih bersih untuk mengimpor seluruh paket; masalah yang diidentifikasi di sini (misalnya java.sql.Datevs java.util.Date) mudah diperbaiki dengan cara lain, tidak juga ditangani oleh impor tertentu dan tentu saja tidak membenarkan impor gila-gilaan gila-gilaan di semua kelas. Tidak ada yang lebih membingungkan daripada membuka file sumber dan harus halaman melalui 100 pernyataan impor.

Melakukan impor tertentu membuat refactoring lebih sulit; jika Anda menghapus / mengganti nama kelas, Anda harus menghapus semua impor spesifiknya. Jika Anda beralih implementasi ke kelas yang berbeda dalam paket yang sama, Anda harus memperbaiki impor. Meskipun langkah-langkah tambahan ini dapat diotomatisasi, mereka benar-benar hit produktivitas tanpa keuntungan nyata.

Bahkan jika Eclipse tidak melakukan impor kelas secara default, semua orang masih akan melakukan impor bintang. Maaf, tetapi benar-benar tidak ada pembenaran rasional untuk melakukan impor tertentu.

Berikut cara mengatasi konflik kelas:

import java.sql.*;
import java.util.*;
import java.sql.Date;
davetron5000
sumber
28
Saya setuju. Meskipun saya tidak akan menentang untuk menggunakan impor eksplisit, saya masih lebih suka menggunakan impor bintang. Mereka menekankan bahwa "unit penggunaan kembali" adalah keseluruhan paket, bukan tipe individualnya. Alasan orang lain yang terdaftar dalam impor bintang lemah, dan dalam pengalaman saya menggunakan impor bintang tidak pernah menyebabkan kesulitan aktual.
Rogério
32
Lihat javadude.com/articles/importondemandisevil.html untuk detail mengapa itu jahat. Gagasan dasar: ini dapat menyebabkan kode berhenti dikompilasi ketika kelas ditambahkan ke paket yang Anda impor (seperti ketika Daftar ditambahkan ke java.util ...)
Scott Stanchfield
61
Semua masalah yang Anda sebutkan dapat diselesaikan dengan IDE modern (menyembunyikan impor, nama kelas refactoring, dll ...).
assylias
15
Saya tidak harus menggunakan IDE untuk membaca atau menulis kode sumber - kode harus dapat dibaca sendiri tanpa alat khusus kecuali bahasanya sangat braindead. Dalam hal ini, Java berfungsi dengan baik - cukup gunakan impor bintang. Tidak ada alasan untuk tidak melakukannya.
davetron5000
42
@ davetron5000 Jika kode Anda berisi 10+ impor wildcard, dan Anda menggunakan kelas Foo, dan jika saya membaca kode Anda tanpa menggunakan IDE (karena argumen Anda adalah bahwa saya tidak harus menggunakannya), bagaimana saya tahu dari mana paket Fooberasal ? Tentu, menggunakan IDE, IDE akan memberi tahu saya, tetapi seluruh argumen Anda adalah bahwa saya harus dapat membaca kode tanpa itu. Melakukan impor eksplisit membantu mendokumentasikan kode (alasan bagus untuk menghindari wildcard) , dan jauh lebih mungkin bahwa saya akan membaca kode tanpa menggunakan IDE, daripada itu saya akan menulis kode tanpa menggunakan IDE.
Andreas
169

silakan lihat artikel saya Impor Sesuai Permintaan Adalah Jahat

Singkatnya, masalah terbesar adalah kode Anda dapat pecah ketika kelas ditambahkan ke paket yang Anda impor. Sebagai contoh:

import java.awt.*;
import java.util.*;

// ...

List list;

Di Jawa 1.1, ini baik-baik saja; Daftar ditemukan di java.awt dan tidak ada konflik.

Sekarang anggaplah Anda memeriksa kode Anda yang berfungsi dengan baik, dan setahun kemudian orang lain mengeluarkannya untuk mengeditnya, dan menggunakan Java 1.2.

Java 1.2 menambahkan antarmuka bernama List ke java.util. LEDAKAN! Konflik. Kode yang berfungsi sempurna tidak lagi berfungsi.

Ini adalah fitur bahasa EVIL . Tidak ada alasan bahwa kode harus berhenti dikompilasi hanya karena tipe ditambahkan ke paket ...

Selain itu, sulit bagi pembaca untuk menentukan "Foo" mana yang Anda gunakan.

Scott Stanchfield
sumber
35
Ini bukan alasan yang valid. Jika Anda mengubah versi java, Anda entah bagaimana mengharapkan beberapa hal gagal, hal yang sama jika Anda mengubah versi biner yang digunakan kode Anda. Dalam kasus ini kode akan melempar kesalahan kompilasi dan itu sepele untuk diperbaiki (lihat jawaban sebelumnya: stackoverflow.com/a/149282/7595 )
Pablo Fernandez
36
@PabloFernandez - Tidak - Jika saya memeriksa kode yang sudah ada di repositori selama setahun, itu masih harus dikompilasi. Import-on-demand dapat dengan mudah gagal ketika kelas baru ditambahkan ke paket yang sudah saya impor. Bukan hanya masalah saat memutakhirkan versi Java. Juga - jika API dirancang dengan baik, seharusnya tidak pernah melanggar kode yang ada pada peningkatan. Satu-satunya waktu saya perlu mengubah kode ketika memutakhirkan versi java adalah karena impor berdasarkan permintaan dan ketika Sun menarik XML API ke dalam runtime java.
Scott Stanchfield
3
MENAMBAHKAN kelas (dengan nama unik, berkualifikasi penuh!) Ke classpath seharusnya tidak mempengaruhi apa pun. Intinya di sini adalah bahwa jika Anda do_not menggunakan sintaks impor-on-demand, itu tidak akan. Jadi, jangan gunakan sintaks buruk yang sayangnya bahasa ini izinkan dan ini adalah satu masalah yang tidak terlalu nyata yang bisa Anda dapatkan.
Scott Stanchfield
28
Inti dari jawaban saya adalah fitur bahasa yang tidak perlu yang menyebabkan masalah. Banyak IDE / editor secara otomatis menangani ekspansi impor. Gunakan impor yang sepenuhnya memenuhi syarat dan tidak ada kemungkinan kesalahan ini terjadi. Saya telah terkena yang satu ini ketika berada di bawah tekanan untuk memperbaiki bug dalam kode yang ada, dan Anda benar-benar tidak memerlukan sesuatu seperti ini sebagai gangguan dari tugas nyata yang ada. java.util.Listvs java.awt.Listtidak terlalu buruk untuk mencari tahu, tetapi cobalah ketika nama kelasnya Configurationdan beberapa perpustakaan dependensi telah menambahkannya dalam versi repo maven terbaru mereka.
Scott Stanchfield
5
Jika saya memperbarui tabung tempat kelas yang saya gunakan kompatibel dengan API-forward, DAN saya tidak menggunakan sintaks impor-atas-permintaan, itu tidak akan mempengaruhi saya sama sekali. Apakah itu masuk akal bagi Anda? Jangan malas mendefinisikan impor dan ini bukan masalah. Sintaks impor-atas-permintaan adalah kesalahan dalam definisi bahasa Java; bahasa yang masuk akal seharusnya tidak memungkinkan kesalahan seperti ini.
Scott Stanchfield
67

Ini tidak buruk untuk menggunakan kartu liar dengan pernyataan impor Java.

Dalam Clean Code , Robert C. Martin sebenarnya merekomendasikan penggunaannya untuk menghindari daftar impor yang panjang.

Berikut rekomendasinya:

J1: Hindari Daftar Impor Panjang dengan Menggunakan Wildcard

Jika Anda menggunakan dua kelas atau lebih dari satu paket, maka impor seluruh paket dengan

paket impor. *;

Daftar panjang impor menakutkan bagi pembaca. Kami tidak ingin mengacaukan puncak modul kami dengan 80 jalur impor. Alih-alih, kami ingin impor menjadi pernyataan singkat tentang paket mana yang kami kolaborasi.

Impor khusus merupakan dependensi yang sulit, sedangkan impor wildcard tidak. Jika Anda secara khusus mengimpor kelas, maka kelas itu harus ada. Tetapi jika Anda mengimpor paket dengan wildcard, tidak ada kelas khusus yang perlu ada. Pernyataan impor hanya menambahkan paket ke jalur pencarian saat mencari nama. Jadi tidak ada ketergantungan yang sebenarnya dibuat oleh impor seperti itu, dan oleh karena itu mereka berfungsi untuk menjaga modul kami kurang ditambah.

Ada kalanya daftar panjang impor tertentu dapat bermanfaat. Misalnya, jika Anda berurusan dengan kode lawas dan Anda ingin mengetahui kelas apa yang Anda butuhkan untuk membuat tiruan dan bertopik, Anda dapat menyusuri daftar impor khusus untuk mengetahui nama-nama yang benar-benar berkualitas dari semua kelas tersebut dan kemudian menempatkan bertopik tepat di tempat. Namun, penggunaan ini untuk impor khusus sangat jarang. Selain itu, sebagian besar IDE modern akan memungkinkan Anda untuk mengonversi impor wildcard ke daftar impor khusus dengan satu perintah. Jadi, bahkan dalam kasus warisan lebih baik mengimpor wildcard.

Impor wildcard kadang-kadang dapat menyebabkan konflik nama dan ambiguitas. Dua kelas dengan nama yang sama, tetapi dalam paket yang berbeda, perlu diimpor secara khusus, atau paling tidak secara khusus memenuhi syarat saat digunakan. Ini bisa menjadi gangguan tetapi cukup jarang bahwa menggunakan impor wildcard umumnya masih lebih baik daripada impor tertentu.

penambang
sumber
41
Saya menyarankan kepada Robert C. Martin untuk menggunakan pola yang lebih baik untuk membuat lebih banyak paket dan kelas sendiri yang tidak memerlukan 80 baris impor. Bahwa banyak kelas yang diperlukan untuk impor di dalam satu kelas hanya memohon 'Entropi, Entropi, hancurkan aku ...' dan tunjukkan alasan untuk menghindari impor * yang diuraikan dalam Scott Stanchfields anwers
Ray
28
Seperti halnya saya umumnya menyukai apa yang dikatakan Paman Bob, dalam hal ini saya juga harus tidak setuju dengannya.
33
Daftar panjang impor menakutkan bagi pembaca. - Pernyataan ini memiliki anggapan yang tidak valid. Pemrogram tidak diharuskan membaca kode sumber dari atas ke bawah. Kami mungkin tidak membaca daftar impor sama sekali. Ketika kami melakukannya, kami mungkin hanya membaca satu dari impor, untuk klarifikasi. Di lain waktu, impor mungkin runtuh seluruhnya, jika kami bekerja dalam IDE. Terlepas dari sumbernya, sekarang ini nasihat yang buruk.
Andy Thomas
10
Hanya untuk memberikan beberapa penyeimbang ketika datang ke mengutip otoritas tentang masalah ini: Google Java Style Guide serta Java Style Guide Twitter (yang sebagian besar didasarkan pada Google one, bersikap adil) secara khusus melarang impor wildcard. Tetapi mereka tidak memberikan alasan untuk keputusan ini.
anothernode
1
Mungkin satu-satunya poin yang saya tidak setuju dalam Kode Bersih. Ini harus menelusuri beberapa baris pernyataan impor atau berjuang untuk menemukan dari mana kelas itu berasal. Saya lebih suka mengidentifikasi dengan mudah dari mana kelas tertentu berasal.
Ganesh Satpute
27

Kinerja : Tidak berdampak pada kinerja karena kode byte sama. meskipun itu akan menyebabkan beberapa overhead kompilasi.

Kompilasi : pada mesin pribadi saya, Mengkompilasi kelas kosong tanpa mengimpor apa pun membutuhkan 100 ms tetapi kelas yang sama ketika mengimpor java. * Membutuhkan 170 ms.

Vinish Nain
sumber
3
import java.*tidak mengimpor apa pun. Mengapa itu membuat perbedaan?
Marquis of Lorne
6
Itu membuat perbedaan karena itu dicari selama kompilasi.
LegendLength
25

Ini mengacaukan namespace Anda, mengharuskan Anda untuk sepenuhnya menentukan nama kelas yang ambigu. Kejadian paling umum dari ini adalah dengan:

import java.util.*;
import java.awt.*;

...
List blah; // Ambiguous, needs to be qualified.

Ini juga membantu membuat dependensi Anda menjadi konkret, karena semua dependensi Anda tercantum di bagian atas file.

hazzen
sumber
20
  1. Ini membantu untuk mengidentifikasi konflik classname: dua kelas dalam paket berbeda yang memiliki nama yang sama. Ini dapat ditutup dengan * import.
  2. Itu membuat dependensi eksplisit, sehingga siapa pun yang harus membaca kode Anda nanti tahu apa yang Anda impor dan apa yang tidak Anda impor.
  3. Itu dapat membuat beberapa kompilasi lebih cepat karena kompiler tidak harus mencari seluruh paket untuk mengidentifikasi depdency, meskipun ini biasanya bukan masalah besar dengan kompiler modern.
  4. Aspek ketidaknyamanan impor eksplisit diminimalkan dengan IDE modern. Sebagian besar IDE memungkinkan Anda untuk menutup bagian impor sehingga tidak menghalangi, secara otomatis mengisi impor ketika dibutuhkan, dan secara otomatis mengidentifikasi impor yang tidak digunakan untuk membantu membersihkannya.

Sebagian besar tempat saya bekerja yang menggunakan sejumlah besar Java membuat impor eksplisit bagian dari standar pengkodean. Saya kadang-kadang masih menggunakan * untuk prototyping cepat dan kemudian memperluas daftar impor (beberapa IDE akan melakukan ini untuk Anda juga) ketika memproduksi kode.

Josh Segall
sumber
Saya suka sebagian besar poin Anda, tetapi itu # 4 khusus yang membuat saya meningkatkan jawaban Anda. IDE modern menghapus sebagian besar argumen yang menentang penggunaan impor eksplisit ...
Sheldon R.
Mungkin bagian dari masalah di sini adalah cara perpustakaan java standar ditata dengan banyak kelas dalam paket yang sama. Berbeda dengan menerapkan lebih dari satu 'prinsip tanggung jawab tunggal' ke suatu paket.
LegendLength
11

Saya lebih suka impor khusus, karena memungkinkan saya untuk melihat semua referensi eksternal yang digunakan dalam file tanpa melihat keseluruhan file. (Ya, saya tahu itu tidak harus menunjukkan referensi yang sepenuhnya memenuhi syarat. Tapi saya menghindarinya jika memungkinkan.)

Jeff C
sumber
9

Dalam proyek sebelumnya saya menemukan bahwa mengubah dari * * impor ke impor tertentu mengurangi waktu kompilasi hingga setengahnya (dari sekitar 10 menit menjadi sekitar 5 menit). * -Import membuat compiler mencari setiap paket yang terdaftar untuk kelas yang cocok dengan yang Anda gunakan. Meskipun saat ini bisa kecil, itu menambah untuk proyek-proyek besar.

Efek samping dari * -import adalah bahwa pengembang akan menyalin dan menempelkan jalur impor umum daripada memikirkan apa yang mereka butuhkan.

Michael Hall
sumber
11
Pasti banyak jalur impor atau sistem pengembangan yang benar - benar menyedihkan untuk ini menjadi kenyataan. Saya menggunakan import- * an saya bisa mengkompilasi seluruh basis kode 2107 kelas saya dalam waktu kurang dari 2 menit.
Lawrence Dol
6

Dalam buku DDD

Dalam teknologi pengembangan apa pun yang akan menjadi dasar penerapannya, cari cara meminimalkan pekerjaan MODULES refactoring. Di Jawa, tidak ada jalan keluar dari mengimpor ke kelas individu, tetapi Anda setidaknya dapat mengimpor seluruh paket pada suatu waktu, mencerminkan niat bahwa paket adalah unit yang sangat kompak sekaligus mengurangi upaya mengubah nama paket.

Dan jika itu mengacaukan namespace lokal itu bukan kesalahan Anda - salahkan ukuran paket.

Ivan
sumber
3
  • Tidak ada dampak runtime, karena kompiler secara otomatis mengganti * dengan nama kelas yang konkret. Jika Anda mendekompilasi file .class, Anda tidak akan pernah melihat import ...*.

  • C # selalu menggunakan * (secara implisit) karena Anda hanya dapat usingmengemas nama. Anda tidak pernah dapat menentukan nama kelas sama sekali. Java memperkenalkan fitur setelah c #. (Java sangat rumit dalam banyak aspek tetapi di luar topik ini).

  • Di Intellij Idea saat Anda "mengatur impor", secara otomatis menggantikan beberapa impor dari paket yang sama dengan *. Ini adalah fitur wajib karena Anda tidak dapat mematikannya (meskipun Anda dapat meningkatkan ambang batas).

  • Kasing yang terdaftar dengan balasan yang diterima tidak valid. Tanpa * Anda masih mendapatkan masalah yang sama. Anda perlu menentukan nama pakcage dalam kode Anda tidak peduli Anda menggunakan * atau tidak.

Leon
sumber
1
Di IntelliJ, ini bukan fitur wajib dan bisa dimatikan.
Bastien7
3

Sebagai catatan: Ketika Anda menambahkan impor, Anda juga menunjukkan dependensi Anda.

Anda dapat melihat dengan cepat apa dependensi file (tidak termasuk kelas dari namespace yang sama).

magallanes
sumber
Setuju. Motivator tidak begitu banyak kinerja atau kompilasi, tetapi keterbacaan kode Anda oleh manusia. Bayangkan saja Anda membaca kode tanpa IDE - di GitHub, misalnya. Tiba-tiba mencari setiap referensi yang tidak didefinisikan dalam file yang Anda baca menjadi membosankan.
Leo Orientis
2

Yang paling penting adalah bahwa mengimpor java.awt.*dapat membuat program Anda tidak kompatibel dengan versi Java di masa depan:

Misalkan Anda memiliki kelas bernama "ABC", Anda menggunakan JDK 8 dan Anda mengimpor java.util.*. Sekarang, anggaplah Java 9 keluar, dan ia memiliki kelas baru dalam paket java.utilyang kebetulan juga disebut "ABC". Program Anda sekarang tidak dapat dikompilasi di Java 9, karena kompiler tidak tahu apakah dengan nama "ABC" yang Anda maksud adalah kelas Anda sendiri atau kelas baru dijava.awt .

Anda tidak akan memiliki masalah ketika Anda hanya mengimpor dari kelas-kelas itu secara eksplisit java.awt yang Anda gunakan.

Sumber:

Impor Java

Affy
sumber
3
tip: Anda bisa menggunakan Streamcontoh kelas baru yang ditambahkan di Java di java.util di Java 8 ...
Clint Eastwood
2

Di antara semua poin valid yang dibuat di kedua sisi saya belum menemukan alasan utama saya untuk menghindari wildcard: Saya ingin dapat membaca kode dan tahu secara langsung apa setiap kelas, atau apakah definisi itu tidak dalam bahasa atau file, di mana menemukannya. Jika lebih dari satu paket diimpor dengan * Saya harus pergi mencari masing-masing untuk menemukan kelas yang tidak saya kenal. Keterbacaan adalah yang tertinggi, dan saya setuju bahwa kode tidak memerlukan IDE untuk membacanya.

pengguna109439
sumber
Jika Anda mengambil kesimpulan logis sepenuhnya maka gaya Anda seharusnya tidak menggunakan impor sama sekali dan alih-alih "new LinkedList" selalu menggunakan "java.util.LinkedList" baru dan lakukan ini secara konsisten di mana-mana .
Erwin Smout