Java içe aktarma deyimi olan bir joker kartı kullanmak neden kötü?


419

Gibi tek bir ifade kullanmak çok daha rahat ve temiz

import java.awt.*;

bir grup bireysel sınıfı içe aktarmaktan

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

İfadede joker karakter kullanmanın nesi yanlış import?

Yanıtlar:


518

Bununla ilgili tek sorun, yerel ad alanınızı tıkamasıdır. Örneğin, bir Swing uygulaması yazdığınızı java.awt.Eventve buna ihtiyaç duyduğunuzu ve şirketin sahip olduğu takvim sistemi ile de arayüz oluşturduğunuzu varsayalım com.mycompany.calendar.Event. Her ikisini de joker karakter yöntemini kullanarak içe aktarırsanız, şu üç şeyden biri gerçekleşir:

  1. Sen arasında salt adlandırma çatışması java.awt.Eventve com.mycompany.calendar.Eventhatta derlemek olamaz ve bu yüzden.
  2. Aslında yalnızca birini içe aktarmayı başarırsınız (iki ithalatınızdan yalnızca biri bunu yapar .*), ancak bu yanlıştır ve kodunuzun neden türün yanlış olduğunu iddia ettiğini anlamakta zorlanırsınız.
  3. Kodunuzu derlediğinizde hayır yoktur com.mycompany.calendar.Event, ancak daha sonra bir tane eklediklerinde, daha önce geçerli olan kodunuz derlenmeyi durdurur.

Tüm ithalatları açıkça listelemenin avantajı, bir bakışta hangi sınıfı kullanmak istediğinizi söyleyebilmemdir, bu da kodu okumayı çok daha kolay hale getirir. Sadece hızlı bir kerelik bir şey yapıyorsanız, açıkça yanlış bir şey yoktur , ancak gelecekteki koruyucular aksi takdirde netliğiniz için size teşekkür edecektir.


7
Bu gerçekleşecek ilk senaryodur. Derleyici iki Event sınıfı olduğunu fark eder ve hata verir.
jan.vdbergh

38
Aşağıdaki yorumumu kontrol ettiğinizden emin olun - zaman içinde üçüncü taraf kütüphanelerine eklenen türlerle ilgili daha büyük bir sorun var. Birisi bağımlı olduğunuz bir kavanoza bir tür ekledikten sonra derlemeyi durduran derleme kodunuz olabilir.
Scott Stanchfield

6
1. konu ile ilgili: teknik olarak derleyebilirsiniz, ancak her seferinde tam sınıf adını kullanmanız gerekir.
Kip

1
Bu tür çatışmaları, her sınıfı açıkça listelemeden çözebilirsiniz, bu da kendi sorunlarına neden olur.
rpjohnst

196

Yıldız ithalatı için bir oy . İçe aktarma ifadesi, bir sınıfı değil, bir paketi içe aktarmayı amaçlamaktadır . Tüm paketleri almak çok daha temiz; burada tanımlanan sorunlar (örn. java.sql.Datevs java.util.Date) diğer yollarla kolayca çözülür, gerçekte değil belirli ithalatlarla ele ve kesinlikle tüm sınıflardaki delice bilgiçlikle ithalatı haklı çıkarmaz. Bir kaynak dosyasını açmaktan ve 100 içe aktarma ifadesini kullanmak zorunda kalmaktan daha rahatsız edici bir şey yoktur.

Belirli ithalatlar yapmak, yeniden düzenlemeyi zorlaştırır; bir sınıfı kaldırır / yeniden adlandırırsanız, sınıfın tüm içe aktarmalarını kaldırmanız gerekir . Bir uygulamayı aynı paketteki farklı bir sınıfa geçirirseniz, içe aktarmaları düzeltmeniz gerekir. Bu ekstra adımlar otomatikleştirilebilse de, gerçek bir kazanç elde etmek için gerçekten verimlilik vuruşlarıdır.

Eclipse varsayılan olarak sınıf ithalatı yapmasa bile, herkes yine de yıldız ithalatı yapıyordu. Üzgünüm, ama belirli ithalatlar yapmak için gerçekten mantıklı bir gerekçe yok.

Sınıf çatışmalarıyla nasıl başa çıkacağınız aşağıda açıklanmıştır:

import java.sql.*;
import java.util.*;
import java.sql.Date;

28
Katılıyorum. Açık ithalat kullanmaya karşı çıkmamama rağmen, yine de yıldız ithalatını kullanmayı tercih ediyorum. "Yeniden kullanım birimi" nin bireysel türleri değil, bütün paket olduğunu vurguluyorlar. Diğerlerinin yıldız ithalatına karşı listelenmesinin nedenleri zayıftır ve yıldız ithalatını kullanma deneyimimde hiçbir zaman gerçek zorluklara neden olmamıştır.
Rogério

32
Neden kötülüğünü öğrenmek için javadude.com/articles/importondemandisevil.html adresine bakın . Temel fikir: içe aktardığınız paketlere sınıf eklendiğinde kodun derlenmesini durdurabilir (Liste java.util'e eklendiğinde olduğu gibi ...)
Scott Stanchfield

61
Bahsettiğiniz tüm problemler modern IDE'ler (ithalatları gizleme, sınıf adını yeniden düzenleme vb.) İle çözülebilir.
assylias

15
Kaynak kodu okumak veya yazmak için bir IDE kullanmamalıyım - dil inanılmaz derecede cesur değilse, kod özel araçlar olmadan kendi başına okunabilir olmalıdır. Bu durumda, Java harika çalışır - sadece yıldız ithalatını kullanın. Yapmamak için hiçbir neden yok.
davetron5000

42
@ davetron5000 Kodunuz 10+ joker karakter içeriyorsa ve sınıf kullanıyorsanız Foove kodunuzu IDE kullanmadan okursam (argümanınız bir tane kullanmak zorunda olmamam gerektiğinden), hangi paketin Foogeldiğini nasıl bileceğim ? Elbette, bir IDE kullanarak IDE bana söyleyecektir, ancak tüm argümanınız kodu bir tane olmadan okuyabilmem gerektiğidir . Açık ithalat yardım yapmak belgeyi kodu (önlemek joker karakterler büyük nedeni) ve ben olacağım daha olasıdır çok var okuma ben olacağım bunun dışında bir IDE kullanmadan kod yazma bir IDE kullanmadan kod.
Andreas

169

lütfen makaleme bakın Talep Üzerine İthalat Kötülük

Kısacası, en büyük sorun, içe aktardığınız pakete bir sınıf eklendiğinde kodunuzun kırılabilmesidir . Örneğin:

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

// ...

List list;

Java 1.1'de bu iyiydi; Java.awt dosyasında liste bulundu ve çakışma olmadı.

Şimdi, mükemmel çalışan kodunuzu kontrol ettiğinizi ve bir yıl sonra bir başkası bunu düzenlemek için çıkardığını ve Java 1.2 kullandığını varsayalım.

Java 1.2, java.util dosyasına List adlı bir arabirim ekledi. BOOM! Fikir ayrılığı. Mükemmel çalışan kod artık çalışmıyor.

Bu bir EVIL dil özelliğidir. Orada YOK kod türü sırf derleme durdurmak gerektiğini sebebi eklenen bir pakete ...

Ayrıca, bir okuyucunun hangi "Foo" kullandığınızı belirlemesini zorlaştırır.


35
Bu geçerli bir mazeret değil. Java sürümünü değiştiriyorsanız, bazı şeylerin başarısız olmasını beklersiniz, kodunuzun kullandığı bir ikili dosyanın sürümünü değiştirirseniz aynı şey. Bu durumlarda kod bir derleme hatası verir ve düzeltilmesi önemsizdir (önceki cevaba bakınız: stackoverflow.com/a/149282/7595 )
Pablo Fernandez

36
@PabloFernandez - Hayır - Bir yıl boyunca depoda olan kodu kontrol edersem, derlemeli. İçe aktardığım mevcut paketlere yeni sınıflar eklendiğinde isteğe bağlı içe aktarma kolayca başarısız olabilir . Java sürümlerini yükseltirken sadece bir sorun değil. Ayrıca - API iyi tasarlanmışsa , yükseltme sırasında mevcut kodu asla kırmamalıdır. Java sürümlerini yükseltirken kodu değiştirmek için gereken tek zaman, istek üzerine içe aktarma ve Sun'ın XML API'lerini java çalışma zamanına çekmesi oldu.
Scott Stanchfield

3
Sınıf yoluna bir sınıf eklemek (benzersiz, tam nitelikli bir adla!) Hiçbir şeyi etkilememelidir. Burada nokta eğer ki do_not kullanım ithalat-on-demand sözdizimi, böyle olmaz. Bu yüzden, dilin maalesef izin verdiği kötü sözdizimini kullanmayın ve bu, vurabileceğiniz daha az gerçek bir sorundur.
Scott Stanchfield

28
Cevabım şu ki, sorunlara neden olan gereksiz bir dil özelliği. Birçok IDE / editör otomatik olarak içe aktarma genişletmesini yönetir. Tam nitelikli içe aktarmalar kullanın ve bu hatanın gerçekleşme şansı yoktur. Mevcut koddaki bir hatayı düzeltmek için baskı altındayken buna çarptım ve eldeki gerçek görevden uzaklaşmak için böyle bir şeye ihtiyacınız yok. java.util.Listvs java.awt.Listanlamak çok kötü değil, ama sınıf adı Configurationve birden fazla bağımlılık kütüphaneleri en son maven repo sürümü ekledikçe deneyin .
Scott Stanchfield

5
Kullandığım sınıfların API-forward uyumlu olduğu ve bir isteğe bağlı içe aktarma sözdizimi kullanmadığım bir kavanoz güncellersem, beni hiç etkilemez. Bu size mantıklı geliyor mu? İthalatın tanımlanmasında tembel olmayın ve bu bir sorun değildir. İsteğe bağlı içe aktarma sözdizimi, Java dilinin tanımında bir hataydı; makul bir dil böyle hatalara izin vermemelidir.
Scott Stanchfield

67

Bu var olmayan bir Java ithalat ifadesiyle vahşi kartı kullanmak için kötü.

Gelen Temiz Kanunu , Robert C. Martin aslında uzun ithalat listeleri önlemek için bunları kullanılmasını önerir.

İşte tavsiye:

J1: Joker Karakter Kullanarak Uzun Alma Listelerinden Kaçının

Bir paketten iki veya daha fazla sınıf kullanıyorsanız, paketin tamamını

ithalat paketi. *;

Uzun ithalat listeleri okuyucu için göz korkutucu. Modüllerimizin tepelerini 80 satırlık ithalatla karıştırmak istemiyoruz. Bunun yerine, ithalatın hangi paketlerle işbirliği yaptığımız konusunda kısa ve öz bir ifade olmasını istiyoruz.

Spesifik ithalatlar zor bağımlılıklardır, ancak joker karakterli ithalatlar değildir. Bir sınıfı özellikle içe aktarıyorsanız, o sınıfın bulunması gerekir. Ancak, joker karakter içeren bir paketi içe aktarırsanız, belirli sınıfların bulunması gerekmez. İçe aktarma ifadesi, isimleri araştırırken paketi arama yoluna ekler. Bu nedenle, bu tür ithalatlar tarafından gerçek bir bağımlılık yaratılmaz ve bu nedenle modüllerimizi daha az bağlı tutarlar.

Belirli ithalatların uzun listesinin yararlı olabileceği zamanlar vardır. Örneğin, eski kodla uğraşıyorsanız ve hangi sınıfları taklit etmek ve saplama yapmak istediğinizi bulmak istiyorsanız, tüm bu sınıfların gerçek nitelikli adlarını bulmak için belirli ithalatların listesini yürütebilir ve daha sonra uygun taslakları yerleştirin. Bununla birlikte, belirli ithalatlar için bu kullanım çok nadirdir. Ayrıca, çoğu modern IDE, joker karakterli içe aktarmaları tek bir komutla belirli bir içe aktarma listesine dönüştürmenize izin verecektir. Bu yüzden eski durumda bile joker karakterler almak daha iyidir.

Joker karakterle içe aktarma bazen ad çakışmaları ve belirsizliklere neden olabilir. Aynı ada sahip ancak farklı paketlerde iki sınıfın özel olarak içe aktarılması veya kullanıldığında en azından özel olarak kalifiye olması gerekir. Bu bir sıkıntı olabilir, ancak joker karakterli ithalat kullanmanın hala belirli ithalatlardan daha iyi olacağı kadar nadirdir.


41
Robert C. Martin'e 80 satırlık ithalat gerektirmeyen daha özlü paketler ve sınıflar oluşturmak için daha iyi kalıplar kullanmasını öneririm. Tek bir sınıf içinde ithalat için gerekli olan birçok sınıf sadece 'Entropy, Entropy, break me please ...' yalvarıyor ve Scott Stanchfields anwers
Ray

28
Genel olarak Bob Amca'nın söylediklerini sevdiğim kadar, bu durumda da aynı fikirde olmam gerekiyor.

33
Uzun ithalat listeleri okuyucu için göz korkutucu. - Bu iddia geçersiz bir varsayım içeriyor. Programcıların kaynak kodunu yukarıdan aşağıya okumaları gerekmez. İçe aktarma listelerini hiç okuyamayız. Bunu yaptığımızda, açıklığa kavuşturmak için ithalatlardan yalnızca birini okuyabiliriz. Diğer zamanlarda, eğer bir IDE'de çalışıyorsak, ithalat tamamen çökebilir. Kaynak ne olursa olsun, bugün bu artık kötü bir tavsiye.
Andy Thomas

10
Sadece bu konuda yetkililere atıfta bulunulduğunda karşı ağırlık sağlamak için: Google Java Stil Kılavuzu ve Twitter'ın Java Stil Kılavuzu (büyük ölçüde Google olanı temel alır, adil olmak için) özellikle joker karakterleri içe aktarmayı yasaklar. Ancak bu karar için herhangi bir gerekçe sağlamazlar.
anothernode

1
Muhtemelen Temiz Kod'da kabul etmediğim tek nokta. Birkaç içe aktarma ifadesi satırında gezinmek veya sınıfın nereden geldiğini bulmakta zorlanıyor. Belirli bir sınıfın nereden geldiğini kolayca tanımlamayı tercih ederim.
Ganesh Satpute

27

Performans : Bayt kodu aynı olduğundan performans üzerinde hiçbir etkisi yoktur. ancak bazı ek yüklere yol açacaktır.

Derleme : kişisel makinemde, hiçbir şey içe aktarmadan boş bir sınıf derlemek 100 ms sürer, ancak içe aktarma java sırasında aynı sınıf.


3
import java.*hiçbir şey ithal etmez. Neden bir fark yaratsın ki?
Lorne Marquis

6
Fark yaratır çünkü derleme sırasında aranır.
LegendLength

25

Belirsiz olan tüm sınıf adlarını tam olarak belirtmenizi gerektiren ad alanınızı karmaşık hale getirir. Bunun en yaygın oluşumu:

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

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

Ayrıca, tüm bağımlılıklarınız dosyanın üstünde listelendiği için bağımlılıklarınızı somut hale getirmenize yardımcı olur.


20
  1. Sınıf adı çakışmalarını tanımlamaya yardımcı olur: farklı paketlerdeki aynı ada sahip iki sınıf. Bu, * içe aktarma ile maskelenebilir.
  2. Bağımlılıkları açık hale getirir, böylece daha sonra kodunuzu okumak zorunda olan herkes neyi almak istediğinizi ve neyi almak istemediğinizi bilir.
  3. Bazı derlemeleri daha hızlı hale getirebilir, çünkü derleyici bütünlükleri tanımlamak için tüm paketi araştırmak zorunda değildir, ancak bu genellikle modern derleyicilerle büyük bir anlaşma değildir.
  4. Açık ithalatın uygunsuz yönleri modern IDE'ler ile en aza indirgenmiştir. Çoğu IDE, içe aktarma bölümünü daraltmadığınız için engellemenize, gerektiğinde içe aktarmaları otomatik olarak doldurmanıza ve kullanılmayan içe aktarmaların temizlenmesine yardımcı olmak için otomatik olarak tanımlamanıza olanak tanır.

Önemli miktarda Java kullanan çalıştığım çoğu yer, açık içe aktarmaları kodlama standardının bir parçası haline getiriyor. Bazen hızlı prototipleme için * kullanıyorum ve daha sonra kodu üretirken içe aktarma listelerini (bazı IDE'ler de sizin için yapacak) genişletiyorum.


Puanlarınızın çoğunu beğendim, ancak özellikle # 4'dü ve cevabınızı onaylamamı sağladı. Modern IDE'ler açık ithalat kullanmaya karşı çoğu argümanı kaldırıyor ...
Sheldon R.

Belki de buradaki sorunun bir kısmı, standart java kütüphanelerinin aynı paket içindeki birçok sınıfla ortaya konma şeklidir. Bir pakete daha fazla 'tek sorumluluk ilkesi' uygulamak yerine.
LegendLength

11

Belirli bir içe aktarmayı tercih ederim, çünkü tüm dosyaya bakmadan dosyada kullanılan tüm dış referansları görmeme izin veriyor. (Evet, tam olarak nitelikli referanslar göstermeyeceğini biliyorum. Ancak mümkün olduğunda bunlardan kaçınıyorum.)


9

Önceki bir projede, * ithalatlarından belirli ithalatlara geçişin derleme süresini yarı yarıya azalttığını (yaklaşık 10 dakikadan yaklaşık 5 dakikaya) buldum. * -İmport, derleyicinin, kullandığınız sınıfla eşleşen bir sınıf için listelenen paketlerin her birinde arama yapmasını sağlar. Bu süre küçük olsa da, büyük projelere katkıda bulunur.

* -İmport'un bir yan etkisi, geliştiricilerin ihtiyaç duydukları şeyleri düşünmek yerine ortak ithalat hatlarını kopyalayıp yapıştırmalarıydı.


11
Bunun doğru olması için çok fazla ithalat hattı veya gerçekten acıklı bir geliştirme sistemi olmalı. Ben import- * bir 2107 sınıfın tüm kod tabanı 2 dakikadan az derleyebilirsiniz .
Lawrence Dol

6

In DDD kitapta

Uygulama hangi temeli temel alacak olursa, MODÜLLERİ yeniden düzenleme çalışmalarını en aza indirmenin yollarını arayın. Java'da, ayrı ayrı sınıflara içe aktarmadan kaçış yoktur, ancak en azından bir seferde tüm paketleri içe aktarabilirsiniz, bu da paket adlarının oldukça uyumlu birimler olma niyetini yansıtırken aynı zamanda paket adlarını değiştirme çabasını da azaltır.

Ve yerel ad alanını tıkarsa, bu sizin hatanız değil - paketin boyutunu suçlayın.


3
  • Derleyici otomatik olarak * somut sınıf adlarıyla değiştirildiği için çalışma zamanı etkisi yoktur. Eğer .class dosyasını kodalıyorsanız, asla göremezsiniz import ...*.

  • C # her zaman yalnızca usingpaket adını kullanabileceğiniz gibi * (örtük olarak) kullanır . Sınıf adını asla belirtemezsiniz. Java özelliği c # 'dan sonra tanıtır. (Java birçok açıdan zor ama bu konunun ötesinde).

  • Intellij Idea'da "içe aktarma düzenleme" yaptığınızda, aynı paketin çoklu içe aktarmalarını * ile otomatik olarak değiştirir. Bu, zorunlu bir özelliktir, çünkü kapatamazsınız (eşiği artırabilirsiniz).

  • Kabul edilen cevapta listelenen vaka geçerli değil. * Olmadan hala aynı sorunu yaşarsınız. * Kullansanız da kullanmasanız da, kodunuzda pakcage adını belirtmeniz gerekir.


1
IntelliJ'de zorunlu bir özellik değildir ve kapatılabilir.
Bastien7

3

Kayıt için: Bir içe aktarma eklediğinizde, bağımlılıklarınızı da belirtirsiniz.

Dosyaların bağımlılıklarının ne olduğunu hızlı bir şekilde görebilirsiniz (aynı ad alanının sınıfları hariç).


Katılıyorum. Motive edici çok fazla performans veya derleme değil, kodunuzun insan tarafından okunabilirliği. Örneğin, GitHub'da IDE'siz bir kod okuduğunuzu düşünün. Birdenbire, okuduğunuz dosyada tanımlı olmayan her referansı aramak, akıl almaz derecede sıkıcı bir hal alır.
Leo Orientis

2

En önemlisi, içe aktarmanın java.awt.*programınızı gelecekteki bir Java sürümüyle uyumsuz hale getirebilmesidir:

"ABC" adlı bir sınıfınız olduğunu, JDK 8 kullandığınızı ve içe aktardığınızı varsayalım java.util.*. Şimdi, Java 9'un ortaya çıktığını ve java.utiltesadüfen "ABC" olarak da adlandırılan yeni bir sınıfı olduğunu varsayalım . Programınız şimdi Java 9'da derlenmeyecektir, çünkü derleyici "ABC" adıyla kendi sınıfınızı veya yeni sınıfınızıjava.awt .

Yalnızca java.awtgerçekten kullandığınız sınıfları açıkça içe aktardığınızda bu sorunla karşılaşmazsınız .

Kaynaklar:

Java İçe Aktarma


3
ipucu: StreamJava 8'de Java'da java.util'de eklenen yeni bir sınıf örneği olarak kullanabilirsiniz ...
Clint Eastwood

2

Her iki tarafta yapılan tüm geçerli noktalar arasında, joker karakterden kaçınmak için ana nedenimi bulamadım: Kodu okuyabilmeyi ve her sınıfın ne olduğunu veya tanımın dilde olmadığını veya dosya, nerede bulunur. Birden fazla paket * ile içe aktarılırsa, tanımadığım bir sınıfı bulmak için her birini aramaya gitmem gerekiyor. Okunabilirlik en üst düzeydedir ve kodu okumak için bir IDE gerektirmemelidir .


Bunu tam mantıksal sonucuna götürürseniz, stiliniz hiç ithalat kullanmamak ve "new LinkedList" yerine her zaman "new java.util.LinkedList" kullanın ve bunu her yerde tutarlı bir şekilde yapın .
Erwin Smout
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.