Java import ステートメントでワイルドカードを使用するのはなぜ悪いのですか?
質問
次のような単一のステートメントを使用する方がはるかに便利でクリーンです。
import java.awt.*;
多数の個別のクラスをインポートするよりも
import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...
でワイルドカードを使用すると何が問題になりますか import
声明?
解決
唯一の問題は、ローカルの名前空間が乱雑になることです。たとえば、Swingアプリを書いているので、 java.awt.Event
が必要で、会社のカレンダーシステム( com.mycompany.calendar .Event
。ワイルドカード方式を使用して両方をインポートすると、次の3つのいずれかが発生します。
-
java.awt.Event
とcom.mycompany.calendar.Event
の間に完全な名前の競合があるため、コンパイルすらできません。 - 実際には1つしかインポートできません(2つのインポートのうち1つだけが
。*
を実行します)が、それは間違っているため、コードが型が間違っていると主張する理由を解明するのに苦労します。 - コードをコンパイルすると
com.mycompany.calendar.Event
はありませんが、後で追加すると、以前の有効なコードが突然コンパイルを停止します。
すべてのインポートを明示的にリストすることの利点は、使用するクラスが一目でわかることです。これにより、コードの読み取りが非常に簡単になります。簡単な1回限りの操作を行っている場合は、明示的に間違っていることはありませんが、将来のメンテナは明確でないことを感謝します。
他のヒント
スターインポートの投票 です。 importステートメントは、クラスではなく、パッケージをインポートすることを目的としています。パッケージ全体をインポートする方がはるかにきれいです。ここで特定された問題(例: java.sql.Date
対 java.util.Date
)は、本当にによって対処されるのではなく、他の手段で簡単に修復できます。特定のインポートであり、確かに、すべてのクラスでめちゃくちゃにつまらないインポートを正当化するものではありません。ソースファイルを開き、100個のインポートステートメントをページングする必要があることほど、混乱することはありません。
特定のインポートを行うと、リファクタリングがより困難になります。クラスを削除/名前を変更する場合、特定のインポートのすべてを削除する必要があります。実装を同じパッケージ内の別のクラスに切り替える場合、インポートを修正する必要があります。これらの追加の手順は自動化できますが、実際には生産性の低下であり、実際の利益はありません。
Eclipseがデフォルトでクラスのインポートを行わなかったとしても、誰もがスターインポートを行っています。申し訳ありませんが、特定のインポートを実行する正当な理由はありません。
クラスの競合に対処する方法は次のとおりです。
import java.sql.*;
import java.util.*;
import java.sql.Date;
私の記事をご覧くださいオンデマンドインポートは悪
要するに、最大の問題は、インポートするパッケージにクラスを追加すると、コードが破損する可能性があるということです。例:
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」を判断するのが難しくなります。使用しています。
その ない Java import ステートメントでワイルドカードを使用するのは不適切です。
で クリーンなコード, 、ロバート C.Martin は実際に、長いインポート リストを避けるためにそれらを使用することを推奨しています。
推奨事項は次のとおりです。
J1:ワイルドカードを使用して、長いインポートリストを避けてください
パッケージから2つ以上のクラスを使用する場合は、パッケージ全体をでインポートします
パッケージをインポートします。*;
輸入の長いリストは、読者にとって困難です。80ラインの輸入品でモジュールの上部を乱雑にしたくありません。むしろ、輸入品がどのパッケージと協力しているかについての簡潔な声明であることを望んでいます。
特定の輸入品は難しい依存関係ですが、ワイルドカードの輸入はそうではありません。クラスを具体的にインポートする場合は、そのクラスが存在する必要があります。ただし、ワイルドカードでパッケージをインポートする場合、特定のクラスが存在する必要はありません。インポートステートメントは、名前を狩るときにパッケージを検索パスに追加するだけです。したがって、そのような輸入によって真の依存関係は作成されないため、モジュールの結合を少なくするのに役立ちます。
特定の輸入品の長いリストが役立つ場合があります。たとえば、レガシーコードを扱っていて、模擬とスタブを構築するために必要なクラスを見つけたい場合は、特定のインポートのリストを下って、それらすべてのクラスの真の適格な名前を見つけてから配置できます。適切なスタブが配置されています。ただし、特定の輸入品のこの使用は非常にまれです。さらに、ほとんどの最新のIDEでは、単一のコマンドを使用して、ワイルドカードのインポートを特定の輸入品のリストに変換することができます。したがって、レガシーの場合でも、ワイルドカードをインポートする方が良いです。
ワイルドカードの輸入は、名前の競合や曖昧さを引き起こすことがあります。同じ名前を持つ2つのクラスですが、異なるパッケージでは、特別にインポートする必要があります。これは迷惑になる可能性がありますが、ワイルドカードの輸入を使用することは、一般に特定の輸入よりも優れているほどまれです。
名前空間が乱雑になるため、あいまいなクラス名を完全に指定する必要があります。これの最も一般的な発生は次のとおりです。
import java.util.*;
import java.awt.*;
...
List blah; // Ambiguous, needs to be qualified.
すべての依存関係がファイルの上部にリストされているため、依存関係を具体的にするのにも役立ちます。
パフォーマンス:バイトコードは同じであるため、パフォーマンスへの影響はありません。 ただし、コンパイルのオーバーヘッドが発生します。
コンパイル:私のパーソナルマシンでは、何もインポートせずに空のクラスをコンパイルするには100ミリ秒かかりますが、java。*のインポートには170ミリ秒かかります。
- クラス名の競合を識別するのに役立ちます:同じ名前を持つ異なるパッケージの2つのクラス。これは* importでマスクできます。
- 依存関係を明示的にするので、後でコードを読まなければならない人は、インポートする意味とインポートする意図がないことを知ることができます。
- コンパイラはパッケージ全体を検索して依存関係を特定する必要がないため、コンパイルを高速化できますが、これは通常、最新のコンパイラでは大した問題ではありません。
- 明示的なインポートの不都合な側面は、最新のIDEで最小限に抑えられています。ほとんどのIDEでは、インポートセクションを折りたたんで邪魔にならないようにし、必要に応じてインポートを自動的に設定し、未使用のインポートを自動的に識別してクリーンアップします。
かなりの量のJavaを使用して作業したほとんどの場所では、明示的なインポートがコーディング標準の一部になっています。コードを製品化するときに、クイックプロトタイピングに*を使用してインポートリストを展開することもあります(一部のIDEでも同様に実行されます)。
特定のインポートを好みます。ファイル全体を見なくても、ファイルで使用されているすべての外部参照を確認できるためです。 (はい、必ずしも完全に修飾された参照が表示されるとは限りません。しかし、可能な限り参照を避けます。)
以前のプロジェクトで、*-importsから特定のインポートに変更すると、コンパイル時間が半分(約10分から約5分)短縮されたことがわかりました。 * -importを使用すると、コンパイラは、リストされている各パッケージで、使用したクラスに一致するクラスを検索します。この時間は短くてもかまいませんが、大きなプロジェクトの場合は合計されます。
*-importの副作用は、開発者が必要なものを考えるのではなく、一般的なインポート行をコピーして貼り付けることでした。
実装の基盤となる開発技術では、最小化する方法を探します モジュールをリファクタリングする作業。 Javaでは、個々のクラスにインポートすることから逃れることはできませんが、 パッケージが非常に凝集性の高いユニットであるという意図を反映して、少なくとも一度にパッケージ全体をインポートできる パッケージ名を変更する労力を同時に減らします。
そして、それがあなたのせいではなくローカルの名前空間を乱雑にしているなら-パッケージのサイズを責めなさい。
最も重要なのは、 java.awt。*
をインポートすると、プログラムが将来のJavaバージョンと互換性がなくなる可能性があることです:
「ABC」という名前のクラスがあり、JDK 8を使用しており、 java.util。*
をインポートするとします。ここで、Java 9が登場し、パッケージ java.util
に新しいクラスがあり、偶然にも" ABC"と呼ばれるとします。コンパイラは名前が「ABC」であるかどうかわからないため、プログラムはJava 9でコンパイルされません。独自のクラスまたは java.awt
の新しいクラスを意味します。
実際に使用する java.awt
から明示的にそれらのクラスのみをインポートする場合、その問題は発生しません。
リソース:
両側で行われたすべての有効なポイントの中で、ワイルドカードを避けるための主な理由が見つかりませんでした。言語またはファイル、それを見つける場所。複数のパッケージが*でインポートされた場合、認識できないクラスを見つけるためにそれらのすべてを検索する必要があります。読みやすさは最高であり、コードを読むためにIDEを必要とするべきではないことに同意します。
-
コンパイラが*を自動的に具体的なクラス名に置き換えるため、実行時の影響はありません。 .classファイルを逆コンパイルすると、
import ... *
は表示されません。 -
C# always は、パッケージ名として
using
しか使用できないため、*(暗黙的に)を使用します。クラス名を指定することはできません。 Javaは、c#の後にこの機能を導入します。 (Javaは多くの面で非常に注意が必要ですが、このトピックを超えています)。 -
Intellij Ideaで「インポートの整理」を行うと、同じパッケージの複数のインポートが自動的に*に置き換えられます。これは必須機能です。オフにすることはできません(ただし、しきい値を増やすことはできます)。
-
受け入れられた返信でリストされたケースは無効です。 *を使用しなくても、同じ問題が発生します。 *を使用するかどうかに関係なく、コードにパッケージ名を指定する必要があります。
レコードの場合: インポートを追加すると、依存関係も示されます。
ファイルの依存関係をすばやく確認できます(同じ名前空間のクラスを除く)。