なぜJavaはクラスの外に関数定義が存在することを許可しないのですか


22

C ++とは異なり、Javaでは、クラス内の関数宣言とクラス外の定義だけを使用することはできません。なぜそうですか?

Javaの単一のファイルにはクラスが1つだけ含まれ、他には何も含まれないことを強調するのですか?



1
定義ヘッダファイルたalaあなたの平均的特性、またはメソッドのシグネチャを行いますか?
デコ

あなたの質問に対する風刺的な答え:steve-yegge.blogspot.com/2006/03/…–
ブランドン

回答:


33

C ++とJavaの違いは、言語がリンクの最小単位と見なすものにあります。

Cはアセンブリと共存するように設計されているため、そのユニットはアドレスによって呼び出されるサブルーチンです。(これは、FORTRANなどのネイティブオブジェクトファイルにコンパイルされる他の言語にも当てはまります。)つまり、関数foo()を含むオブジェクトファイルには、次の_fooようなアドレスのみに解決されるシンボルが呼び出されます。0xdeadbeefリンク中。それだけです。関数が引数を取る場合、アドレスを呼び出す前に関数が期待するすべてが正しいことを確認するのは呼び出し側の責任です。通常、これはスタックに物を積み上げることによって行われ、コンパイラーはうんざりする作業を処理し、プロトタイプが一致することを確認します。オブジェクトファイル間でこれをチェックすることはありません。呼び出しリンクを間違えた場合、呼び出しは計画どおりに終了することはなく、警告も表示されません。危険にもかかわらず、これにより、複数の言語(アセンブリを含む)からコンパイルされたオブジェクトファイルを、大した手間をかけずに機能するプログラムにリンクできます。

C ++は、すべての追加の空想にもかかわらず、同じように機能します。コンパイラは、名前空間、クラス、およびメソッド/メンバー/などをシューホーンします。クラスの内容を単一の名前にフラット化することにより、この規則に準拠します。単一の名前は、クラスを一意にする方法でマングルされます。たとえば、次のようなメソッドは、実行時のように、オブジェクトファイルとアドレスに入れFoo::bar(int baz)られると混乱する可能性があります。これは完全にコンパイラに依存しているため、異なるマングリングスキームを持つ2つのオブジェクトをリンクしようとすると、運が悪くなります。これはUいですが、ブロックを使用してマングリングを無効にできるため、他の言語からC ++コードに簡単にアクセスできるようになります。C ++は、主にネイティブオブジェクト形式で許可されているため、Cからフリーフローティング関数の概念を継承しました。_ZN4Foo4barEi0xBADCAFEextern "C"

Javaは、独自のオブジェクトファイル形式である.classファイルを持つ、絶縁された世界に住んでいる別の獣です。クラスファイルには、そのコンテンツに関する豊富な情報が含まれいるため、ネイティブリンケージメカニズムでは夢にも思わなかった実行時のクラスの処理を環境で実行できます。その情報はどこかから始めなければならず、その出発点はclass。入手可能な情報により、コンパイルされたコードは、C、C ++、または他の言語のようにソースコードの説明を含む個別のファイルを必要とせずに、それ自体を説明できます。これにより、実行時でもネイティブリンケージ不足を使用するすべてのタイプセーフティの利点が得られます。また、リフレクションを使用してファイルから任意のクラスをフィッシングし、何かが一致しない場合にエラーを保証して使用できます。

まだ理解していない場合、この安全性にはすべてトレードオフが伴います。JavaプログラムにリンクするものはすべてJavaでなければなりません。(「リンク」とは、あるクラスファイル内の何かが別のクラスファイル内の何かを参照することを意味します。)JNIを使用してネイティブコードにリンクできますが、ネイティブサイドを壊すと暗黙のコントラクトがあります、あなたは両方のピースを所有しています。

Javaは大きく、Adaが過去10年間にあったように、最初に導入された時点で利用可能なハードウェア上で特に高速ではありませんでした。Jim Goslingだけが、クラスJavaのリンクの最小単位を作成する動機が何であるかを確実に言うことができますが、フリーフローターを追加することでランタイムに追加される余分な複雑さが取り引きのキラーであったかもしれないと推測しなければなりません。


リンク壊れ、Webアーカイブのリンクを取得
noɥʇʎԀʎzɐɹƆ

14

答えは、Wikipediaによれば、Javaはシンプルでオブジェクト指向になるように設計されていると思います。関数は、それらが定義されているクラスを操作するためのものです。その考え方では、クラス外に関数を置くことは意味がありません。Javaは純粋なOOPに適合しなかったため、Javaが許可しないという結論に飛び込みます。

Googleで簡単に検索しても、Java言語の設計の動機はあまりわかりませんでした。


6
プリミティブ型の存在は、Javaを「純粋なOOP」から除外しないのですか?
ラドゥムルゼア

5
@SoboLAN:はい。機能を追加すると、「純粋なOOP」がさらに少なくなります。
ジョルジオ

8
「純粋なOOP」の定義に依存する@SoboLAN。いくつかの時点で、すべてが...すべての後に、コンピュータメモリにビットの組み合わせである
jwenting

3
@jwenting:さて、プログラミング言語の抽象化の目的は、基礎となるビットをできるだけ隠すことです。プログラムでこれらのビットを見るほど、プログラミング言語が漏れやすい抽象化を提供していると考え始める必要があります(意図的に金属の近くに構築されていない限り)。そのため、すべてがビットの操作に要約されますが、異なる言語は異なるレベルの抽象化を提供します。そうしないと、アセンブリをより高いレベルの言語と区別できなくなります。
ジョルジオ

2
「純粋なOOP」の定義に依存します。すべてのデータ値はオブジェクトであり、すべてのデータ操作はメッセージの受け渡しを通じて取得される結果メソッド呼び出しです。私の知る限り、これ以上のものはありません。
ジョルジオ

11

本当の質問は、C ++のやり方で物事を続けることのメリットは何でしょうか、ヘッダーファイルの本来の目的は何ですか?簡単な答えは、ヘッダーファイルスタイルにより、多くのクラスが同じ型を参照する可能性のある大規模プロジェクトでのコンパイル時間を短縮できることです。これは、コンパイラの性質上、JAVAおよび.NETでは必要ありません。

こちらの回答をご覧ください:ヘッダーファイルは実際に良いですか?


1
+1、実際には、ヘッダーファイルが提供するパブリックインターフェイスとプライベート実装を分離するのが好きだという人がいると聞きましたが。もちろん、それらの人々は間違っています。;)
vaughandroid

6
@Baqueta Javaでは、interfaceand class:)でこれを実現できます。ヘッダーは不要です!
アンドレスF.

1
単純なクラスインスタンスがどのように機能するかを理解するために3つ以上のファイルを見なければならないことの望ましさを私は決して理解しません。
エリックReppen

@ErikReppen通常、インターフェイス(またはCのヘッダーファイル)は、顧客がソリューションを書き込むためにユーザーが読み取り可能な形式で取得するものであり、残りはバイナリ形式のみで提供されます(もちろんJavaでは、ソースを提供する必要はありません)インターフェースのクラスファイルとjavadocが行います)。
14

@jwenting私はそのケースを考えていませんでした。私はこの愚かなコードベースを維持しなければならない次の貧しいろくでなしの考えにもっと慣れています。 -go-roundアーキテクチャ。
エリックReppen

3

Javaファイルはクラスを表します。クラス外にプロシージャがある場合、スコープはどうなりますか?グローバルになりますか?または、Javaファイルが表すクラスに属しますか?

おそらく、あなたは理由のために別のファイルの代わりにそのJavaファイルにそれを入れました-それは他のどのクラスよりもそのクラスと一緒に行くからです。クラス外のプロシージャが実際にそのクラスに関連付けられている場合、それが属するクラス内に強制的に移動させてみませんか?Javaはこれをクラス内の静的メソッドとして処理します。

外部クラスプロシージャが許可された場合、おそらく宣言されたファイルを持つクラスへの特別なアクセス権がないため、データを変更しないユーティリティ関数に制限されます。

このJavaの制限の唯一の欠点は、クラスに関連付けられていないグローバルプロシージャが本当にある場合、それらを保持するMyGlobalsクラスを作成し、それらのプロシージャを使用する他のすべてのファイルにそのクラスをインポートすることです。

実際、Javaインポートメカニズムが機能するには、この制限が必要です。すべてのAPIが利用できるため、javaコンパイラはコンパイル対象とコンパイル対象を正確に知る必要があります。したがって、ファイルの先頭に明示的なimportステートメントがあります。グローバルを人工的なクラスにグループ化することなく、クラスパス上のすべてのグローバルではなくグローバルをコンパイルするようにJavaコンパイラに指示するにはどうすればよいですか?doStuff()と他の誰かがdoStuff()を持っている場合の名前空間の衝突はどうですか?動作しません。MyClass.doStuff()およびYourClass.doStuff()を指定するように強制すると、これらの問題が修正されます。プロシージャを外部ではなくMyClass内に強制的に移動すると、この制限が明確になり、コードに追加の制限が課されることはありません。

Javaには多くの問題があります-シリアライゼーションには非常に多くの小さないぼがあり、それを使用するにはほとんど難しすぎる(SerialVersionUIDを考えてください)また、シングルトンやその他の一般的なデザインパターンを破るのにも使用できます。Objectのclone()メソッドは、deepClone()とshallowClone()に分割され、タイプセーフである必要があります。すべてのAPIクラスはデフォルトで不変にできた可能性があります(Scalaでの方法)。ただし、すべてのプロシージャがクラスに属している必要があるという制限は適切です。主に、面倒な制限を課すことなく、言語とコードを単純化して明確にするのに役立ちます。


3

答えた人のほとんどと彼らの有権者は質問を誤解していると思います。彼らはC ++を知らないことを反映しています。

「定義」と「宣言」は、C ++で非常に具体的な意味を持つ単語です。

OPは、Javaの動作を変更することを意味するものではありません。これは純粋に構文に関する質問です。有効な質問だと思います。

C ++には、メンバー関数を定義する 2つの方法があります

最初の方法はJavaの方法です。すべてのコードを中括弧内に入れるだけです。

class Box {
public:
    // definition of member function
    void change(int newInt) { 
        this._m = newInt;
    }
private:
    int _m
}

2番目の方法:

class Box {
public:  
    // declaration of member function
    void change(int newInt); 
private:
    int _m
}

// definition of member function
// this can be in the same file as the declaration
void Box::change(int newInt) {
    this._m = newInt;
}

両方のプログラムは同じです。この関数changeはまだメンバー関数です。クラスの外部には存在しません。さらに、クラス定義には、Javaの場合と同様に、すべてのメンバー関数と変数の名前と型を含める必要があります。

Jonathan Hensonは、これがC ++でヘッダーが機能する方法の成果物であることは正しいです。ヘッダーファイルに宣言を配置し、別の.cppファイルに実装を配置して、プログラムがODR(One Definition Rule)に違反しないようにします。しかし、それ以外にもメリットがあります。大きなクラスのインターフェイスを一目で確認できます。

Javaでは、この効果を抽象クラスまたはインターフェースで近似できますが、それらを実装クラスと同じ名前にすることはできないため、かなり不器用になります。


そして、それは両方とも人もJavaも理解していないことを示しています。同じパッケージ内にない限り、インターフェイスと実装に同じ名前を使用できます。
jwenting 14

パッケージ(または名前空間、または外部クラス)が名前の一部であると考えています。
エリックヴァンヴェルゼン

2

これは、クラスローディングメカニズムの成果物だと思います。各クラスファイルは、ロード可能なオブジェクトのコンテナです。クラスファイルの「外側」の場所はありません。


1
ソースコードを別のユニットにまとめると、クラスファイル形式をそのまま保持できなくなる理由はわかりません。
マット

クラスファイルとソースファイルの間には1:1の対応関係があります。これは、システム全体でのより優れた設計決定の1つです。
ddyer

@ddyer Foo $ 2 $ 1.classを見たことがありませんか?(Java内部クラスクラスファイル名を参照)

それは「onto」マッピングになりますか?いずれの場合でも、各クラスは正確に1つのソースファイルをコンパイルすることで生成されます。これは適切な設計上の決定です。
ddyer

0

Javaに非常によく似たC#には、部分メソッドが排他的に使用されることを除いて、部分メソッドを使用したこの種の機能があります。

部分メソッド:http : //msdn.microsoft.com/en-us/library/6b0scde8.aspx

部分的なクラスとメソッド:http : //msdn.microsoft.com/en-us/library/wa80x488.aspx

Javaで同じことができなかった理由はわかりませんが、おそらく、この機能を言語に追加するユーザーベースからのニーズが認識されているかどうかにかかっています。

C#のほとんどのコード生成ツールは部分クラスを生成するため、開発者は必要に応じて、別のファイルのクラスに手動で記述したコードを簡単に追加できます。


0

C ++では、クラスのテキスト全体を、そのメンバーを使用するか、インスタンスを生成するすべてのコンパイル単位の一部としてコンパイルする必要があります。コンパイル時間を正常に保つ唯一の方法は、クラスのテキストに、可能な限り、実際にそのコンシューマーに必要なものだけを含めることです。C ++メソッドがそれらを含むクラスの外部で記述されることが多いという事実は、クラスが使用されるすべてのコンパイルユニットに対してコンパイラがすべてのクラスメソッドのテキストを1回処理する必要があるという事実によって動機付けられた非常に厄介なハックです非常識なビルド時間。

Javaでは、コンパイルされたクラスファイルには、特にC ++ .hファイルとほぼ同等の情報が含まれています。クラスのコンシューマは、コンパイラに.javaファイルを処理させることなく、必要なすべての情報をそのファイルから抽出できます。.hファイルに含まれるクラスの実装とクライアントの両方が利用できる情報が含まれるC ++とは異なり、Javaのフローは逆になります。クライアントが使用するファイルは、のコンパイルに使用されるソースファイルではありませんクラスコードですが、代わりにクラスコードファイルの情報を使用してコンパイラによって生成されます。クライアントが必要とする情報を含むファイルと実装を含むファイルとの間でクラスコードを分割する必要がないため、Javaはそのような分割を許可しません。


-1

その一部は、Javaが大規模なチームでの使用に関係する保護主義的な言語だということです。クラスは上書きまたは再定義できません。メソッドを使用できる方法と使用できない方法を非常に具体的に定義する4レベルのアクセス修飾子があります。すべてが強力/静的に型付けされており、開発者を他人または自分自身によって引き起こされる型の不一致から保護します。クラスと関数を最小単位として持つことで、アプリの設計方法のパラダイムを大幅に再発明しやすくなります。

二重目的の名詞/動詞のファーストクラス関数が降雨し、破られた開いたピニャータからキャンディのように渡されるJavaScriptと比較して、クラスに相当する関数コンストラクターは、プロトタイプを変更して新しいプロパティを追加したり、インスタンスの古いプロパティを変更したりできます既にいつでも作成されているため、コンストラクトを独自のバージョンに置き換えることを妨げるものは絶対にありません.5つのタイプがあり、さまざまな状況下で自動変換および自動評価が行われます。

function nounAndVerb(){
}
nounAndVerb.newProperty = 'egads!';

最終的には、ニッチと市場についてです。JavaScriptは、JavaがかつてWeb UIを作成しようとする開発者の小さなグループの手にあったのと同じくらい適切で、100人(その多くは平凡な開発者)の手に悲惨です。100人の開発者と作業するとき、最後に望むのは、ひどいパラダイムを再発明する1人の男です。UIを使用している場合、または迅速な開発がより重要な懸念事項である場合、最後にしたいことは、比較的簡単なことをすばやく行うことに障害があることです。

しかし、結局のところ、これらはどちらも一般的な汎用言語なので、そこには少し哲学的な議論があります。私のJavaとC#での最大の個人的な経験は、ほとんどの開発者が基本的なOOPの価値を理解していると思われるレガシーコードベースを見たことも、これまで働いたこともないことです。クラスにすべてをラップしなければならないゲームに入ると、おそらく、3〜5行のクラスの大規模なチェーンを装った機能スパゲッティではなく、OOPをしていると仮定する方が簡単です。

とはいえ、危険であることが十分にわかっていて、それを誇示することを恐れない誰かが書いたJavaScriptほどひどいものはありません。それが一般的な考え方だと思います。その男は、おそらくJavaでほぼ同じルールで遊ぶ必要があります。私は、ろくでなしは常に道を見つけると主張します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.