私は最近Javaを学び始めましたが、すべてのJavaクラスを個別のファイルで宣言する必要があるのは非常に奇妙でした。私はC#プログラマーですが、C#は宣言しません。そのような制限を適用します。
なぜJavaこれを行うのですか?設計上の考慮事項はありましたか?
編集(いくつかの回答に基づく):
なぜJava IDEの時代にこの制限を削除しないのですか?これは既存のコードを壊しません(またはそうしますか?)。
Java言語仕様、第3版 によると:
この制限は、コンパイル単位ごとに最大で1つのそのようなタイプが存在する必要があることを意味します。 この制限により、Javaプログラミング言語またはJava仮想マシンの実装)のコンパイラが簡単に見つけることができます。パッケージ内の名前付きクラス;たとえば、パブリックタイプwet.sprocket.Toadのソースコードは、ディレクトリwet/sprocketのToad.Javaファイルにあります。オブジェクトコードは、同じディレクトリのToad.classファイルにあります。
強調は私のものです。
基本的に、OSのディレクトリ区切り文字を名前空間のドットに、またはその逆に変換したかったようです。
そうです、それはある種の設計上の考慮事項でした。
私はC#ソリューションを採用し、これを実行して(複数のパブリッククラスが含まれているファイルをすべて削除し)、それらを個々のファイルに分割しました。これにより、作業がはるかに簡単になりました。
ファイルに複数のパブリッククラスがある場合、いくつかの問題があります。
ファイルの名前は何ですか?公開クラスの1つ?別の名前?人々は、不十分なソリューションコード編成とファイル命名規則に関して十分な問題を抱えており、1つの追加の問題があります。
また、ファイル/プロジェクトエクスプローラーを参照しているときは、物事が隠されていないのは良いことです。たとえば、1つのファイルを表示してドリルダウンすると、200のクラスがすべて一緒にマッシュアップされます。 1つのファイルと1つのクラスがある場合は、テストをより適切に整理し、ソリューションの構造と複雑さを感じることができます。
Javaこれは正しいと思います。
から 考えるJava
:
コンパイル単位(ファイル)ごとに存在できるパブリッククラスは1つだけです。
各コンパイルユニットには、そのパブリッククラスで表される単一のパブリックインターフェイスがあるという考え方です。必要な数の「フレンドリーな」クラスをサポートできます。コンパイルユニット内に複数のパブリッククラスがある場合、コンパイラはエラーメッセージを表示します。
パッケージがファイルシステム(?7.2.1)に保存されている場合、ホストシステムは選択可能コンパイル時エラーであるという制限を適用しますタイプ名と拡張子(.Javaや.javなど)で構成される名前のファイルでタイプが見つからない場合、次のいずれかが当てはまる場合:
- 型は、型が宣言されているパッケージの他のコンパイル単位のコードによって参照されます。
- この型はパブリックとして宣言されています(したがって、他のパッケージのコードからアクセスできる可能性があります)。
- この制限は、コンパイル単位ごとに最大で1つのそのようなタイプが存在する必要があることを意味します。
- この制限により、Javaプログラミング言語またはJava仮想マシンの実装)のコンパイラが簡単に見つけることができます。パッケージ内の名前付きクラス;たとえば、パブリックタイプwet.sprocket.Toadのソースコードは、ディレクトリwet/sprocketのToad.Javaファイルにあります。オブジェクトコードは、同じディレクトリのToad.classファイルにあります。
つまり、クラスパスにすべてをロードしなくてもクラスを見つけることができるかもしれません。
編集:「maychoose」はnotにその制限に従う可能性を残しているようで、「may」の意味はおそらく RFC 2119 (つまり「オプション」)に記載されているものです。 ")
しかし実際には、これは非常に多くのプラットフォームで実施され、非常に多くのツールとIDE)に依存しているため、を選択する「ホストシステム」は表示されません。 notその制限を適用します。
「 オークに一度... 」から
非常に明白です-ほとんどの場合、設計上の理由がわかれば-コンパイラは、すべてのコンパイルユニット(.Javaファイル)をさらにパスして、どのクラスがどこにあるかを把握する必要があります、それはコンパイルをさらに遅くします。
(注意:
Oakバージョン0.2のOak言語仕様 (スクリプト後のドキュメント):Oakは、現在一般にJavaとして知られているものの元の名前でした。このマニュアルは、Oak(つまり、Java)で利用できる最も古いマニュアルです。
Javaの起源に関する詳細な歴史については、 グリーンプロジェクト および Java(TM)テクノロジー:初期の歴史 をご覧ください。
)
Javaは、開発者の観点から単純さを念頭に置いて作成されたという意味で、混乱を避けるです。「プライマリ」クラスはパブリッククラスであり、それらが同じ名前のファイルにあり、クラスのパッケージで指定されたディレクトリにある場合、(人間が)簡単に見つけることができます。
Java言語は90年代半ばに開発され、IDEがコードナビゲーションと簡単な検索を行う前の時代であることを思い出してください。
クラスが他の1つのクラスによってのみ使用される場合は、それをプライベート内部クラスにします。このようにして、ファイルに複数のクラスがあります。
クラスが他の複数のクラスによって使用されている場合、これらのクラスのどれを同じファイルに入れますか? 3つすべて?すべてのクラスを1つのファイルにまとめることになります...
それが言語設計者がそれをすることに決めた方法です。主な理由は、コンパイラのパススルーを最適化することだったと思います。コンパイラは、パブリッククラスを見つけるためにファイルを推測したり解析したりする必要はありません。それは実際には良いことだと思います。コードファイルを見つけやすくし、1つのファイルに多くを入れすぎないようにする必要があります。また、Javaによって、コードファイルをパッケージと同じディレクトリ構造に配置する必要があります。これにより、コードファイルを簡単に見つけることができます。
1つのファイルに複数のJavaトップレベルクラスを含めることは技術的に合法です。ただし、これは悪い習慣と見なされ、多くのJavaツール(IDEを含む)これを行うと機能しません。
JLSはこれを言います:
パッケージがファイルシステムに保存されている場合(§7.2.1)、ホストシステムは制限を適用することを選択できますタイプがファイルシステムの下に見つからない場合はコンパイル時エラーです次のいずれかに該当する場合は、タイプ名と拡張子(.Javaや.javなど)で構成される名前。
- 型は、型が宣言されているパッケージの他のコンパイル単位のコードによって参照されます。
- この型はパブリックとして宣言されています(したがって、他のパッケージのコードからアクセスできる可能性があります)。
JLSテキストでのmayの使用に注意してください。これは、コンパイラがこれを無効として拒否する場合としない場合があることを示しています。 Javaコードを移植可能に構築しようとしている場合ソースコードレベルで。したがって、1つのソースファイルに複数のクラスがある場合でも、これは適切な状況ではありません。開発プラットフォームで動作します。これを行うのは悪い習慣です。
私の理解では、この「拒否の許可」は、より広い範囲のプラットフォームでJavaを実装しやすくすることを目的とした設計上の決定です一部。 (逆に)JLSでは、すべてのコンパイラが複数のクラスを含むソースファイルをサポートする必要があり、ファイルシステムベースではないプラットフォームでJavaを実装するという概念上の問題が発生します。
実際には、経験豊富なJava開発者は、これを実行できることを見逃すことはありません。モジュール化と情報隠蔽は、パッケージ、クラスアクセス修飾子、内部クラスまたはネストされたクラスの適切な組み合わせを使用して行う方が適切です。
なぜJava IDEの時代にこの制限を削除しないのですか?これは既存のコードを壊しません(またはそうしますか?)。
これで、すべてのコードが統一されました。ソースファイルを見ると、何が期待できるかがわかります。すべてのプロジェクトで同じです。 Javaがこの規則を削除した場合、作業するすべてのプロジェクトのコード構造を再学習する必要がありますが、今は一度学習してどこにでも適用できます。すべてにIDEを信頼するべきではありません。
実は、これはJava言語仕様(セクション7.6、ページ番号209)によるオプションの制限ですが、必須の制限としてOracle Javaコンパイラ)が続きます。 。Java言語仕様によると、
パッケージがファイルシステムに保存されている場合(§7.2.1)、ホストシステムは、タイプ名とタイプ名で構成される名前のファイルにタイプが見つからない場合、コンパイル時エラーであるという制限を適用することを選択できます。次のいずれかに該当する場合は、拡張子(.Javaや.javなど)。
- 型は、型が宣言されているパッケージの他のコンパイル単位のコードによって参照されます。
- この型はパブリックとして宣言されています(したがって、他のパッケージのコードからアクセスできる可能性があります)。
この制限は、コンパイル単位ごとに最大で1つのそのようなタイプが存在する必要があることを意味します。この制限により、Javaコンパイラはパッケージ内の名前付きクラスを簡単に見つけることができます。
実際には、多くのプログラマーは、パブリックであるか、他のコンパイルユニットのコードによって参照されているかに関係なく、各クラスまたはインターフェイスタイプを独自のコンパイルユニットに配置することを選択します。
たとえば、パブリックタイプwet.sprocket.Toadのソースコードは、ディレクトリwet/sprocketのファイルToad.Javaにあり、対応するオブジェクトコードは、同じディレクトリのファイルToad.classにあります。
より明確に理解するために、同じソースファイルに2つのパブリッククラスパブリッククラスAとパブリッククラスBがあり、AクラスがまだコンパイルされていないクラスBを参照していると想像してください。そして、クラスAをコンパイル(コンパイル-リンク-ロード)しています。クラスBにリンクしている間、クラスBには特定のB.Javaファイルがないため、現在のパッケージ内の各* .Javaファイルを強制的に検査するようになります。したがって、上記の場合、コンパイラがどのクラスがどのソースファイルの下にあり、どのクラスにメインメソッドがあるかを見つけるのに少し時間がかかります。したがって、ソースファイルごとに1つのパブリッククラスを保持する理由は、リンク(インポートステートメント)中にソースファイルとコンパイル済みファイルをより効率的に検索できるため、実際にコンパイルプロセスを高速化するためです。クラスの名前がわかっていれば、クラスパスエントリごとにどこにあるかがわかっているので、インデックスを作成する必要はありません。
また、アプリケーションを実行するとすぐに、JVMはデフォルトでパブリッククラスを検索し(制限がなく、どこからでもアクセスできるため)、そのパブリッククラスでpublic static void main(String args[])
も検索します。パブリッククラスは、Javaアプリケーション(プログラム)のJVMインスタンスが開始される最初のクラスとして機能します。したがって、プログラムに複数のパブリッククラスを提供すると、コンパイラ自体がスローすることで停止します。エラー。これは、public static void main(String args[])
を持つパブリッククラスが1つだけJVMの初期クラスであるため、後でどのクラスを初期クラスにするかについてJVMを混乱させることができないためです。
実際には質問に対する答えではありませんが、それでもデータポイントです。
私は個人のC++ユーティリティライブラリのヘッダーをgrepし( ここ から自分で取得できます)、実際にクラスを宣言するほとんどすべてのヘッダーファイル(一部は無料の関数を宣言するだけです)は複数のクラスを宣言します。私は自分自身をかなり優れたC++デザイナーだと思っています(ライブラリは場所によっては少し厄介ですが、私はその唯一のユーザーです)。したがって、少なくともC++の場合、同じファイル内の複数のクラスは正常であると提案します。そして良い習慣さえ。
1つのファイルに多くのパブリッククラスが存在する可能性があります。ただし、ファイルごとの最上位クラス。ファイルごとに、必要な数のパブリック内部/ネストされたクラスを含めることができます。
私はあなたがこのリンクが好きだと思います-- Javaファイルは複数のクラスを持つことができますか?
これにより、Foobar.classからFoobar.Javaに移行するためのより単純なヒューリスティックが可能になります。
Foobarが任意のJavaファイルにある可能性がある場合は、マッピングの問題があります。これは、最終的には、すべてのJavaファイルをフルスキャンしてクラスの定義。
個人的には、これが、Javaアプリケーションが非常に大きくなり、それでも頑丈になる可能性があるという結果をもたらす奇妙なルールの1つであることがわかりました。