Javaインポート文でワイルドカードを使用するのはなぜ悪いのですか?


419

次のような単一のステートメントを使用する方がはるかに便利でクリーンです

import java.awt.*;

個々のクラスの束をインポートするよりも

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

importステートメントでワイルドカードを使用することの何が問題になっていますか?

回答:


518

唯一の問題は、ローカルの名前空間が乱雑になることです。たとえば、Swingアプリを作成しているためにが必要java.awt.Eventであり、会社のカレンダーシステムとインターフェースしているとしますcom.mycompany.calendar.Event。ワイルドカードを使用して両方をインポートすると、次の3つのいずれかが発生します。

  1. あなたは間のあからさまな名前の競合持つjava.awt.Eventとしcom.mycompany.calendar.Eventて、あなたもコンパイルすることはできませんので。
  2. 実際に管理できるのは1つだけです(2つのインポートの1つだけがインポートします.*)が、それは間違ったものであり、コードが型を間違っていると主張している理由を理解するのに苦労しています。
  3. コードをコンパイルするとはありませんcom.mycompany.calendar.Eventが、後でコードを追加すると、以前の有効なコードは突然コンパイルを停止します。

すべてのインポートを明示的にリストすることの利点は、どのクラスを使用するつもりだったかが一目でわかるため、単純にコードの読み取りがはるかに簡単になることです。簡単な1回限りのことをしている場合は、特に問題はありませんが、将来のメンテナはそれ以外の場合は明確にしてくれることに感謝します。


7
これが最初に起こるシナリオです。コンパイラは2つのイベントクラスがあることを認識し、エラーを出します。
jan.vdbergh 2008

38
下記の私のコメントを必ず確認してください-時間の経過とともにタイプがサードパーティのライブラリに追加されることにより大きな問題があります。依存しているjarに型が追加された後にコンパイルを停止するコンパイルコードを使用できます。
スコットスタンフィールド

6
問題1に関しては、技術的にはコンパイルできますが、毎回完全修飾クラス名を使用する必要があります。
キップ

1
すべてのクラスを明示的にリストしなくても、この種の競合を解決できます。これにより、独自の問題が発生します。
rpjohnst 2011

196

スターインポートへの投票です。importステートメントは、クラスではなくパッケージをインポートすることを目的としています。パッケージ全体をインポートする方がずっときれいです。ここで特定された問題(java.sql.Datevsなどjava.util.Date)は、他の方法で簡単に修正でき、特定のインポートでは実際に対処されず、すべてのクラスでめちゃくちゃなインポートを正当化するものではありません。ソースファイルを開いて、100のインポートステートメントをページングする必要があること以外に当惑することはありません。

特定のインポートを行うと、リファクタリングがより困難になります。クラスを削除/名前変更する場合は、その特定のインポートをすべて削除する必要があります。同じパッケージ内の別のクラスに実装を切り替える場合、インポートを修正する必要があります。これらの追加の手順は自動化できますが、実際には生産性への影響はありません。

Eclipseがデフォルトでクラスのインポートを行わなかったとしても、誰もがスターインポートを実行することになります。申し訳ありませんが、特定のインポートを行う正当な理由はありません。

クラスの競合に対処する方法は次のとおりです。

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

28
同意する。明示的なインポートを使用することに反対するわけではありませんが、依然としてスターインポートを使用することを好みます。彼らは、「再利用の単位」はパッケージ全体であり、個々のタイプではないことを強調しています。スターインポートに対してリストされた他の理由が弱いため、スターインポートを使用した私の経験では、実際に問題を引き起こしたことはありません。
–Rogério

32
参照してくださいjavadude.com/articles/importondemandisevil.htmlそれは悪だ理由を詳細については。基本的な考え方:インポートしたパッケージにクラスを追加すると(リストがjava.util ...に追加されたときなど)、コードのコンパイルが停止する可能性があります
Scott Stanchfield

61
あなたが言及するすべての問題は、最新のIDE(インポートの非表示、クラス名のリファクタリングなど)によって解決できます。
assylias 2013

15
IDEを使用してソースコードを読み書きする必要はありません。言語が信じられないほど頭の痛くない限り、コードは特別なツールなしでそれ自体で読み取り可能であるべきです。この場合、Javaはうまく機能します-スターインポートを使用するだけです。しない理由はありません。
davetron5000 2013

42
@ davetron5000コードに10個以上のワイルドカードインポートが含まれていて、class Fooを使用し、IDEを使用せずにコードを読み取った場合(引数はIDEを使用する必要はないため)、どのパッケージFooからのものであるかをどのようにして知ることができますか??確かに、IDEを使用すると、IDEが教えてくれますが、あなたの全体的な主張は、私がコードなしでコードを読むことができるはずだということです。明示的な輸入のヘルプやってドキュメントをコード (ワイルドカード避けるための大きな理由)、そして、それは多くの可能性が高い私がすることがありますことだ読んで、私はできるだろうということよりも、IDEを使用せずに、コードを書く IDEを使用せずに、コードを。
アンドレアス

169

私の記事を参照してくださいインポートオンデマンドは悪です

つまり、最大の問題は、インポートするパッケージにクラスが追加されると、コードが壊れる可能性があるということです。例えば:

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

// ...

List list;

Java 1.1では、これで問題ありませんでした。リストはjava.awtで見つかり、競合はありませんでした。

次に、完全に機能するコードをチェックインし、1年後に誰かがそれを編集のために持ち出し、Java 1.2を使用しているとします。

Java 1.2は、Listという名前のインターフェースをjava.utilに追加しました。ブーム!紛争。完全に機能するコードは機能しなくなります。

これはEVIL言語の機能です。ありませんNOタイプがされているので、コードだけでコンパイルを停止する必要があることを理由追加パッケージには...

さらに、読者が使用している「Foo」を判別するのが難しくなります。


35
これは有効な言い訳ではありません。Javaのバージョンを変更する場合、何らかの理由で失敗することが予想されます。コードが使用するバイナリのバージョンを変更した場合も同様です。これらの場合、コードはコンパイルエラーをスローし、修正するのは簡単です(前の回答を参照:stackoverflow.com/a/149282/7595
Pablo Fernandez

36
@PabloFernandez-いいえ-リポジトリに1年間保管されているコードをチェックアウトしても、コンパイルされるはずです。インポートした既存のパッケージに新しいクラスが追加されると、オンデマンドインポートが失敗する可能性があります。これは、Javaバージョンをアップグレードするときの問題ではありません。また、APIが適切に設計されていれば、アップグレード時に既存のコードが壊れることはありません。Javaバージョンのアップグレード時にコードを変更する必要があったのは、インポートオンデマンドのためと、SunがXML APIをJavaランタイムにプルしたときだけでした。
Scott Stanchfield、

3
クラスパス(一意の完全修飾名!)をクラスパスに追加しても、何の影響もありません。ここでのポイントは、あなたがあればということであるdo_not輸入・オン・デマンドの構文を使用し、それはしません。ですから、残念なことに言語が許容する悪い構文を使用しないでください。これは、あなたが遭遇する可能性がある非常に現実的な問題ではありません。
Scott Stanchfield、

28
私の回答の要点は、問題を引き起こすのは不必要な言語機能であるということです。多くのIDE /エディターは自動的にインポート拡張を処理します。完全修飾インポートを使用してください。この特定のエラーが発生する可能性はありません。既存のコードのバグを修正するよう圧力をかけられているときに私はこの問題に遭遇しました。実際のタスクの邪魔になるため、このようなものは本当に必要ありません。java.util.Listvs java.awt.Listはそれほど悪くはありませんが、クラス名がでConfiguration、複数の依存ライブラリが最新のmavenリポジトリバージョンに追加したときに試してください。
Scott Stanchfield

5
使用するクラスがAPI順互換であるjarを更新し、かつ、インポートオンデマンド構文を使用しない場合、影響はまったくありません。それはあなたにとって意味がありますか?インポートの定義に怠惰にしないでください。これは問題ではありません。インポートオンデマンドの構文は、Java言語の定義の誤りでした。合理的な言語では、このようなエラーは許されません。
Scott Stanchfield

67

Javaインポート文でワイルドカードを使用すること悪くありません

きれいなコード、ロバートC.マーティンは、実際に長いインポートリストを避けるためにそれらを使用することをお勧めします。

ここに推奨事項があります:

J1:ワイルドカードを使用して長いインポートリストを回避する

パッケージから2つ以上のクラスを使用する場合は、パッケージ全体を

パッケージをインポートします。*;

インポートの長いリストは読者にとって困難です。80行のインポートでモジュールの上部を乱雑にしたくありません。むしろ、どのパッケージと共同作業するかについての簡潔なステートメントをインポートしたいのです。

特定のインポートはハードな依存関係ですが、ワイルドカードインポートはそうではありません。特定のクラスをインポートする場合は、そのクラスが存在している必要があります。ただし、ワイルドカードを使用してパッケージをインポートする場合、特定のクラスが存在する必要はありません。importステートメントは、名前を探すときにパッケージを検索パスに追加するだけです。そのため、そのようなインポートによって真の依存関係が作成されることはなく、したがって、それらはモジュールの結合を低く保つのに役立ちます。

特定のインポートの長いリストが役立つ場合があります。たとえば、レガシーコードを処理していて、モックとスタブを構築するために必要なクラスを見つけたい場合は、特定のインポートのリストをたどって、それらすべてのクラスの実際の修飾名を見つけて、適切なスタブを配置します。ただし、この特定のインポートの使用は非常にまれです。さらに、最近のほとんどのIDEでは、ワイルドカードを使用したインポートを1つのコマンドで特定のインポートのリストに変換できます。したがって、レガシーの場合でも、ワイルドカードをインポートすることをお勧めします。

ワイルドカードをインポートすると、名前の競合やあいまいさが生じる場合があります。同じ名前で2つのクラスが異なるパッケージにある場合、使用する場合は、具体的にインポートするか、少なくとも具体的に修飾する必要があります。これは厄介なことですが、ワイルドカードインポートを使用する方が一般的に特定のインポートよりも優れているため、まれです。


41
Robert C. Martinに、より良いパターンを使用して、80行のインポートを必要としない、より簡潔な独自のパッケージとクラスを作成することをお勧めします。スコットStanchfieldsのanwersに概説されている単一のクラス内のインポートのために必要な多くのクラスは、単に「...私にしてください破る、エントロピーをエントロピー」を物乞いされ、回避のインポートに理由を指す*さん
レイ

28
ボブおじさんが言わなければならないことが一般的に好きなのと同じくらい、この場合、私も彼に同意しなければなりません。

33
インポートの長いリストは読者にとって困難です。-このアサーションには無効な推定があります。プログラマはソースコードを上から下に読む必要はありません。インポートリストをまったく読み取れない場合があります。その際、明確にするために、インポートの1つだけを読み取る場合があります。また、IDEで作業している場合、インポートが完全に折りたたまれる場合もあります。ソースに関係なく、今日これは悪いアドバイスです。
アンディトーマス

10
この問題について当局を引用する際のカウンターウェイトを提供するために:Google Java Style GuideTwitterのJava Style Guide(これは主にGoogleのものに基づいていますが、公平に言えば)は、特にワイルドカードのインポートを禁止しています。しかし、彼らはこの決定の根拠を提供していません。
anothernode

1
たぶん私がクリーンコードで同意しなかった唯一の点。インポート文の数行をスクロールする必要があるか、クラスがどこから来ているのかを見つけるのに苦労しています。特定のクラスがどこから来ているのかを簡単に識別したい。
Ganesh Satpute

27

パフォーマンス:バイトコードは同じであるため、パフォーマンスへの影響はありません。ただし、コンパイルのオーバーヘッドが発生します。

コンパイル:私のマシンでは、何もインポートせずに空のクラスをコンパイルすると100ミリ秒かかりますが、java。*をインポートすると同じクラスに170ミリ秒かかります。


3
import java.*何もインポートしません。なぜ違いが出るのでしょうか?
ローン侯爵、2015年

6
コンパイル時に検索されるため、違いがあります。
LegendLength 2016年

25

名前空間が乱雑になり、あいまいなクラス名を完全に指定する必要があります。この最も一般的な発生は次のとおりです。

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

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

また、依存関係はすべてファイルの先頭にリストされるため、依存関係を具体化するのにも役立ちます。


20
  1. これは、クラス名の競合を特定するのに役立ちます:同じ名前を持つ異なるパッケージの2つのクラス。これは*インポートでマスクできます。
  2. 依存関係を明示的にするので、後でコードを読む必要がある人は誰でも、インポートする意味とインポートするつもりがなかったことを知ることができます。
  3. コンパイラーは依存関係を特定するためにパッケージ全体を検索する必要がないため、コンパイルを高速化できますが、これは通常、最新のコンパイラーでは大したことではありません。
  4. 最新のIDEでは、明示的なインポートの不便な面が最小限に抑えられています。ほとんどのIDEでは、インポートセクションを折りたたんで邪魔にならないようにしたり、必要に応じてインポートを自動的に入力したり、未使用のインポートを自動的に識別してクリーンアップしたりできます。

かなりの量のJavaを使用する私がこれまでに働いたほとんどの場所では、明示的なインポートがコーディング標準の一部となっています。私は時々、プロトタイピングを迅速に行うために*を使用し、コードを製品化するときにインポートリストを展開します(一部のIDEもこれを行います)。


私はあなたのほとんどの点が好きですが、具体的にはあなたの答えを賛成するようになったのは#4でした。最新のIDEは、明示的なインポートの使用に対するほとんどの議論を取り除きます...
シェルドンR.

おそらく、ここでの問題の一部は、標準Javaライブラリが同じパッケージ内の多くのクラスでレイアウトされる方法にあります。より多くの「単一の責任原則」をパッケージに適用するのではなく。
LegendLength 2016年

11

ファイル全体を見ることなく、ファイルで使用されているすべての外部参照を確認できるため、特定のインポートを好みます。(はい、私はそれが完全に修飾された参照を必ずしも表示しないことを知っています。しかし、可能な限りそれらを避けます。)


9

以前のプロジェクトで、*-importsから特定のインポートに変更すると、コンパイル時間が半分(約10分から約5分)に短縮されることがわかりました。* -importを使用すると、コンパイラーは、リストされた各パッケージを検索して、使用したものと一致するクラスを探します。この時間は短くてもかまいませんが、大規模なプロジェクトでは合計になります。

* -importの副作用は、開発者が必要なものについて考えるのではなく、一般的なインポート行をコピーして貼り付けることでした。


11
されている必要があり、多くの輸入ラインのか、本当に哀れ真であることが、このために開発システム。import- *を使用すると、2107クラスのコードベース全体を2分以内にコンパイルできます。
Lawrence Dol

6

ではDDDブック

実装の基礎となる開発テクノロジーでは、MODULESのリファクタリング作業を最小限に抑える方法を探します。Javaでは、個々のクラスへのインポートからの脱出はありませんが、パッケージは非常にまとまりのある単位であると同時にパッケージ名を変更する労力を削減するという意図を反映して、少なくとも一度にパッケージ全体をインポートできます。

そして、それがローカル名前空間を散らかすならば、それはあなたの責任ではありません-パッケージのサイズのせいです。


3
  • コンパイラーが*を具象クラス名に自動的に置き換えるため、ランタイムへの影響はありません。.classファイルを逆コンパイルしても、は表示されませんimport ...*

  • パッケージ名のみを使用できるため、C#は常に *(暗黙的に)を使用しますusing。クラス名を指定することはできません。Javaはc#の後に機能を導入します。(Javaは多くの面で非常に扱いにくいですが、このトピックを超えています)。

  • Intellij Ideaで「インポートの整理」を行うと、同じパッケージの複数のインポートが自動的に*に置き換えられます。オフにすることはできないため、これは必須の機能です(しきい値を増やすことはできます)。

  • 承認された返信に記載されているケースは無効です。*がなくても、同じ問題が発生します。*を使用するかどうかに関係なく、コードにパッケージ名を指定する必要があります。


1
IntelliJでは、これは必須の機能ではなく、オフにすることができます。
Bastien7

3

記録のために:インポートを追加するときは、依存関係も示しています。

ファイルの依存関係(同じ名前空間のクラスを除く)をすばやく確認できます。


同意します。動機は、それほどパフォーマンスやコンパイルではなく、コードの可読性です。IDEなしでコードを読んでいると想像してみてください-たとえば、GitHubで。読んでいるファイルで定義されていないすべての参照を突然検索することは、非常に面倒な作業になります。
レオオリエン

2

最も重要なのは、インポートjava.awt.*によってプログラムが将来のJavaバージョンと互換性を失う可能性があることです。

「ABC」という名前のクラスがあり、JDK 8を使用していて、をインポートするとしますjava.util.*。さて、Java 9が出て、java.util偶然にも "ABC"と呼ばれる新しいクラスがパッケージにあるとします。プログラムはJava 9でコンパイルされません。コンパイラーは、「ABC」という名前が自分のクラスか、の新しいクラスかを知らないためですjava.awt

java.awt実際に使用するクラスから明示的にそれらのクラスのみをインポートする場合は、この問題は発生しません。

リソース:

Javaインポート


3
ヒント:StreamJava 8のjava.utilでJavaに追加された新しいクラスの例として使用できます...
Clint Eastwood

2

両側で行われたすべての有効なポイントの中で、ワイルドカードを回避する主な理由はわかりませんでした。コードを読んで、すべてのクラスが何であるかを直接知りたい、またはその定義が言語にないか、ファイル、それを見つける場所。複数のパッケージが*でインポートされる場合、私はそれらすべてを検索して、認識できないクラスを見つける必要があります。読みやすさは最高で、コードを読むためにIDEを必要としないことにも同意します。


それを完全に論理的な結論に導く場合、スタイルはインポートをまったく使用せず、「新しいLinkedList」の代わりに常に「新しいjava.util.LinkedList」を使用して、これをどこでも一貫し行う必要があります。
Erwin Smout 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.