web-dev-qa-db-ja.com

Java importステートメントでワイルドカードを使用するのはなぜ悪いのですか?

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

import Java.awt.*;

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

import Java.awt.Panel;
import Java.awt.Graphics;
import Java.awt.Canvas;
...

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

355
jnancheta

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

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

すべてのインポートを明示的にリストすることの利点は、使用するクラスが一目でわかるため、コードの読み取りがはるかに簡単になることです。一度だけの簡単なことをしている場合、明示的にwrongはありませんが、将来のメンテナーは明快に感謝します。

436

これが投票ですfor star imports。 importステートメントは、クラスではなく、packageをインポートすることを目的としています。パッケージ全体をインポートする方がはるかにきれいです。ここで特定された問題(例:Java.sql.Date vs Java.util.Date)は、特定のインポートではreallyではなく、他の方法で簡単に修正できます。ソースファイルを開き、100個のimportステートメントをページングする必要があることほど困惑することはありません。

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

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

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

import Java.sql.*;
import Java.util.*;
import Java.sql.Date;
167
davetron5000

私の記事をご覧ください Import on Demand is Evil

要するに、最大の問題は、インポートするパッケージにクラスをaddedすると、コードが破損する可能性があることです。例えば:

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理由は、型がaddedパッケージへ...

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

152

Java importステートメントでワイルドカードを使用するのは悪いnotです。

Clean Code では、ロバートC.マーティンは、長いインポートリストを避けるために実際に使用することを推奨しています。

推奨事項は次のとおりです。

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

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

インポートパッケージ。*;

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

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

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

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

63
hwiechers

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

import Java.util.*;
import Java.awt.*;

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

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

22
hazzen

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

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

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

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

14
Josh Segall

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

10
Jeff C

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

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

9
Michael Hall

In DDDブック

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

そして、それがあなたのせいではなくローカルの名前空間を乱雑にしているなら-パッケージのサイズを責めなさい。

6
Ivan

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

2
user109439

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

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

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

リソース:

Javaインポート

2
Virtual
  • コンパイラは*を自動的に具体的なクラス名に置き換えるため、実行時への影響はありません。 .classファイルを逆コンパイルすると、import ...*は表示されません。

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

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

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

2
Leon

記録の場合:インポートを追加すると、依存関係も示されます。

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

0
magallanes