Javaは、C ++のように、クラス定義と実装の分離を促進しますか?


10

私は宿題をしていて、GRASPの「保護されたバリエーション」に従って、どちらのアプローチがより良いかを評価する必要があります。Stack Overflowで、C ++でのヘッダーファイルとコードファイルの分離に関する質問を見つけまし

ただし、Javaがクラス定義とクラス実装の分離を促進する際にC ++に従わない理由を知りたいのですが。C ++メソッドに比べて、Javaメソッドには利点がありますか?


「Javaがヘッダーファイルを使用しない理由」を確認したい場合は、それを確認し、「どちらが良いか」を排除してください。これまで見てきたように、私たちはそれにアレルギーがあります;)また、検索して、これ(または少なくとも密接に関連する質問)が以前に出されたことはかなり確かです。

おっと、リンクが機能しませんでした。基本的に知りたいのは、両者を区別することと、コードの再利用や拡張性のためにどちらがより簡単になる傾向があるかということです。
エティエンヌ・ノエル

1
Cが作成された当時のワンパスコンパイラテクノロジーには制限があるため、C(およびC ++の拡張)は実際にはヘッダーファイルを実装ファイルから分離するしかありませんでした。
Channel72、

2
問題のクラスがインターフェースを実装している場合、Javaは、クラス定義とクラス実装を分離できるインターフェースを持つことができます。C ++とまったく同じではありません。
FrustratedWithFormsDesigner

1
また、C ++ヘッダーファイルは、PIMPLイディオムを使用しない限り、私が望むよりもかなり多くの実装を公開しています。であっても、すべてのデータメンバーをリストする必要があるprivateため、実装はサイズとprivateメンバー関数も認識します。
David Thornley、2011年

回答:


13

次のプログラムには何行のコードがありますか?

#include <iostream>

int main()
{
   std::cout << "Hello, world!\n";
   return 0;
}

おそらく7と答えます(空白行を数えなかった場合は6、中かっこを数えなかった場合は4)。

ただし、コンパイラは非常に異なるものを認識します。

~$ cpp hello.cpp | wc
  18736   40822  437015

はい、これは「Hello、world!」の18.7 KLOCです。プログラム。C ++コンパイラはそれらすべてを解析する必要があります。これが、C ++のコンパイルに他の言語に比べて時間がかかる主な理由であり、現代の言語がヘッダーファイルを避けている主な理由です。

より良い質問は

なぜない Cは、ヘッダファイルを持っ++?

C ++はCのスーパーセットとして設計されたため、下位互換性のためにヘッダーファイルを保持する必要がありました。

では、なぜCにヘッダーファイルがあるのでしょうか。

その原始的な別々のコンパイルモデルのために。Cコンパイラによって生成されたオブジェクトファイルには型情報が含まれていないため、型エラーを防ぐために、ソースコードにこの情報を含める必要があります。

~$ cat sqrtdemo.c 
int main(void)
{
    /* implicit declaration int sqrt(int) */
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm -Dsqrt= sqrtdemo.c
sqrtdemo.c: In function main’:
sqrtdemo.c:5:5: warning: implicit declaration of function printf [-Wimplicit-function-declaration]
sqrtdemo.c:5:5: warning: incompatible implicit declaration of built-in function printf [enabled by default]
~$ ./a.out 
2.000000

適切な型宣言を追加すると、バグが修正されます。

~$ cat sqrtdemo.c 
#undef printf
#undef sqrt

int printf(const char*, ...);
double sqrt(double);

int main(void)
{
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm sqrtdemo.c
~$ ./a.out 
1.414214

がないことに注意してください#include。しかし、多くの外部関数(ほとんどのプログラムで使用されます)を使用する場合、それらを手動で宣言するのは面倒でエラーが発生しやすくなります。ヘッダーファイルを使用する方がはるかに簡単です。

現代の言語はどのようにしてヘッダーファイルを回避できますか?

タイプ情報を含む別のオブジェクトファイル形式を使用する。たとえば、Java * .classファイル形式には、フィールドとメソッドパラメータのタイプを指定する「記述子」が含まれています。

これは新しい発明ではありませんでした。以前(1987年)、Borlandが個別にコンパイルされた「ユニット」をTurbo Pascal 4.0に追加したとき、ヘッダーファイルの必要性をなくすために*.TPU、Turbo Cではなく新しいフォーマットを使用することを選択しました*.OBJ


おもしろいですが、Turbo Pascalをs OBJではなく出力ファイルに設定できると確信していますTPU...
CVn

7

Javaには、コントラクトを定義するためのインターフェースがあります。これにより、呼び出し元が必要とするものと実際の実装からより高いレベルの抽象化が得られます。つまり、呼び出し元は実装クラスを知る必要はなく、サポートするコントラクトを知るだけで済みます。

Mapのすべてのキー/値を遅くするメソッドを書きたいとしましょう。

public static <K,V> void printMap(Map<K,V> map) {
    for(Entry<K,V> entry: map.entrySet())
        System.out.println(entry);
}

このメソッドは、それを実装するクラスから削除された抽象インターフェースでentrySet()を呼び出すことができます。このメソッドを呼び出すことができます。

printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());

1
そこのプログラマへようこそピーター-私は名前を認識したと思った:-)。さらに、Javaの抽象基本クラスもコントラクトを定義していると主張する人もいますが、それはおそらく別のスレッドに対する議論です。
Martijn Verburg、

こんにちは@MartijnVerburg、ニース追加。抽象クラスは、実装のないインターフェースと具象クラスの違いを曖昧にすると思います。拡張メソッドは、区別をさらにぼやけさせます。できるだけシンプルなインターフェイスを使用することを好む傾向があります。
Peter Lawrey

そうですね、Javaは、パブリックコントラクトを定義するための複数の方法を持つScalaの道を歩み始めます-それが良いことかどうかはまだわかりません:-)
Martijn Verburg

-1これはC ++でも可能で、単純な#define interface class
シェード

@Sjoerd C ++メソッドvirtualがポリモーフィズムを取得するためにキーワードを使用する必要がなくなることを知りませんでした。Javaのように1つまたは2つの具象型を使用するだけの場合、これはパフォーマンスを低下させません。これがC ++でどのように機能するかについてのドキュメントを教えてもらえますか?
Peter Lawrey、2011年

5

ヘッダーは、歴史的な事故として、率直に言って存在します。それは信じられないほど貧弱なシステムであり、他の言語にはそれほどひどいものはありません。それらに対処する必要がない人は喜ぶべきです。


3

ヘッダーは、個別のコンパイルを可能にするためにあります。ヘッダーを#includeすることにより、コンパイラーはコンパイルされたC ++コードのバイナリー構造について何も知る必要がなく、その仕事を別のリンカーに任せることができます。Javaはそのコンパイラーで別個のリンカーを使用せず、.classファイルは厳密に定義されているため、コンパイラーはそれらを読み取って、すべてのメンバーをすべてのタイプで判別でき、各コンパイル単位でそれらを再宣言する必要はありません。

C ++ヘッダーにすべての実装を含めることができますが、#includeが実行されるたびにコンパイラーがそれを再コンパイルするため、リンカーは重複するコピーを整理して破棄する必要があります。


0

Javaはクラスの定義と実装の分離を促進しますが、それはあなたがどこから見ているかに依存します。

Javaクラスの作成者は、クラスの定義とその実装を1つのファイルで確認できます。これにより、クラスを維持するために1つの場所に行くだけでよく、2つのファイル(C ++のように.hと.cpp)を切り替える必要がないので、開発プロセスが簡素化されます。ただし、クラスのコンシューマーである場合は、.jarまたはスタンドアロンの.classにパッケージ化されている.classファイルを介して、定義のみを扱います。

C ++では、定義と実装を分離できますが、アドホックです。たとえば、ヘッダーファイル内にメソッドをインラインで記述するのを止めることは何もありません。テンプレートクラスの場合、これは必須です。ヘッダーファイルには、メンバー変数もリストされます。メンバー変数は、クラスの実装の詳細であり、コンシューマーとは関係ありませんが、ヘッダーファイルを見ると誰でも見ることができます。

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