回答:
それらの間には大きな違いがあります。C ++では、ジェネリック型のクラスまたはインターフェイスを指定する必要はありません。そのため、より緩いタイピングの警告を使用して、真に総称的な関数とクラスを作成できます。
template <typename T> T sum(T a, T b) { return a + b; }
上記のメソッドは、同じタイプの2つのオブジェクトを追加し、「+」演算子を使用できる任意のタイプTに使用できます。
Javaでは、次のように、渡されたオブジェクトのメソッドを呼び出す場合はタイプを指定する必要があります。
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
C ++では、コンパイラーは(呼び出された)異なる型に対して異なる関数を生成するため、汎用関数/クラスはヘッダーでのみ定義できます。そのため、コンパイルは遅くなります。Javaではコンパイルに大きなペナルティはありませんが、Javaは「消去」と呼ばれる手法を使用します。この場合、ジェネリック型は実行時に消去されるため、実行時にJavaは実際に...
Something sum(Something a, Something b) { return a.add ( b ); }
したがって、Javaでの一般的なプログラミングは実際には有用ではなく、新しいforeach構文を支援するためのほんの少しの構文上の砂糖にすぎません。
編集:有用性に関する上記の意見は若い自己によって書かれました。もちろん、Javaのジェネリックスはタイプセーフを支援します。
extends
super
Java GenericsはC ++テンプレートとは大きく異なります。
基本的にC ++では、テンプレートは基本的に栄光のプリプロセッサ/マクロセット(注:類推を理解できない人もいるので、テンプレート処理がマクロであるとは言っていません)。Javaでは、それらは基本的にオブジェクトのボイラープレートキャストを最小限に抑えるための構文糖です。これは、C ++テンプレートとJavaジェネリックスのかなりまともな紹介です。
この点について詳しく説明します。C++テンプレートを使用する場合、基本的には、#define
マクロを使用する場合と同じように、コードの別のコピーを作成します。これによりint
、配列のサイズなどを決定するテンプレート定義にパラメーターを設定するなどのことができます。
Javaはそのように動作しません。Javaでは、すべてのオブジェクトがjava.lang.Objectから拡張されるため、Genericsより前のコードは次のようになります。
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
すべてのJavaコレクション型が基本型としてObjectを使用したため、それらに何でも入れることができるからです。Java 5はロールオーバーしてジェネリックを追加するため、次のようなことができます。
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
そして、これがすべてのJava Genericsです。オブジェクトをキャストするためのラッパーです。これは、Java Genericsが洗練されていないためです。彼らは型消去を使用します。この決定が行われたのは、Java Genericsの登場が遅れたため、下位互換性を壊したくなかったためです(a Map<String, String>
はいつでも使用できます)Map
が呼び出されたは)。型消去が使用されていないところ.NET / C#にこれを比較し、その差異のすべての種類につながる(例えば、あなたがプリミティブ型を使用してすることができますIEnumerable
し、IEnumerable<T>
お互いに何の関係を負いません)。
また、Java 5+コンパイラーでコンパイルされたジェネリックを使用するクラスは、JDK 1.4で使用できます(Java 5+を必要とする他の機能またはクラスを使用しない場合)。
これが、Java Genericsが構文糖と呼ばれる理由です。ます。
しかし、ジェネリックの作成方法に関するこの決定は非常に大きな影響を与えるため、Javaジェネリックについての(すばらしい)JavaジェネリックFAQは、人々が持つ多くの多くの質問に答えるようになっています。
C ++テンプレートには、Java Genericsにはないいくつかの機能があります。
プリミティブ型引数の使用。
例えば:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Javaでは、ジェネリックスでプリミティブ型の引数を使用できません。
デフォルトの型引数の使用。これは、Javaには欠けている機能の1つですが、これには下位互換性の理由があります。
例えば:
public class ObservableList<T extends List> {
...
}
異なる引数を持つテンプレート呼び出しは実際に異なる型であることを強調する必要があります。静的メンバーを共有することすらありません。Javaではこれは当てはまりません。
ジェネリックとの違いは別として、完全を期すために、C ++とJava(およびもう1つ)の基本的な比較を示します)のます。
また、Javaで考えることも提案できます。C ++プログラマーとして、オブジェクトのような概念の多くはすでに第二の性質ですが、微妙な違いがあるため、部品を読み飛ばしたとしても、導入テキストを持つことは価値があります。
Javaを学ぶときに学ぶことの多くはすべてのライブラリー(両方とも標準(JDKに含まれるもの)と非標準(Springなどの一般的に使用されるものを含む))です。Java構文はC ++構文よりも詳細で、C ++の機能(演算子のオーバーロード、多重継承、デストラクタメカニズムなど)は多くありませんが、厳密にはC ++のサブセットになりません。
Map map = new HashMap<String, String>
。これは、古いコードを新しいJVMにデプロイでき、バイトコードが類似しているため実行できることを意味します。
C ++にはテンプレートがあります。Javaにはジェネリックがあり、C ++テンプレートのように見えますが、非常に異なります。
テンプレートは、その名前が示すように、コンパイラーに(それを待つ...)テンプレートを提供することによって機能します。テンプレートは、テンプレートパラメーターを入力することでタイプセーフなコードを生成するために使用できます。
ジェネリックスは、私が理解しているように、逆の方法で機能します。型パラメーターはコンパイラーによって使用され、それらを使用するコードがタイプセーフであることを確認しますが、結果のコードは型なしで生成されます。
C ++テンプレートは非常に優れたマクロシステムであり、Javaジェネリックはタイプキャストを自動生成するツールであると考えてください。
const
。C ++のオブジェクトはconst
、const
-nessがキャストされない限り、ポインターを介して変更されません。同様に、Javaのジェネリック型によって作成された暗黙のキャストは、型パラメーターがコードのどこかに手動でキャストされない限り、「安全」であることが保証されています。
C ++テンプレートのもう1つの機能は、Javaジェネリックにはない特化です。これにより、特定のタイプに対して異なる実装を行うことができます。したがって、たとえば、intの高度に最適化されたバージョンを保持しながら、残りの型の汎用バージョンを保持できます。または、ポインタ型と非ポインタ型の異なるバージョンを使用できます。これは、ポインターが渡されたときに逆参照されたオブジェクトを操作する場合に便利です。
このトピックの優れた説明は、Javaジェネリックとコレクション 、Philips WadlerによるMaurice Naftalinにあります。私はこの本を強くお勧めします。引用するには:
JavaのジェネリックスはC ++のテンプレートに似ています。...構文は意図的に類似しており、セマンティクスは意図的に異なります。...意味的には、Javaジェネリックは消去によって定義され、C ++テンプレートは拡張によって定義されます。
ここで完全な説明を読んでください。
(ソース:oreilly.com)
基本的に、AFAIK、C ++テンプレートは各タイプのコードのコピーを作成しますが、Javaジェネリックはまったく同じコードを使用します。
はい、あなたは C ++テンプレートがJavaジェネリックの概念と同等であると言うことができます(より適切にはJavaジェネリックは概念のC ++と同等であると言うことですが)
C ++のテンプレートメカニズムに精通している場合、ジェネリックスは類似していると思うかもしれませんが、類似性は表面的なものです。ジェネリックスは、特殊化ごとに新しいクラスを生成することも、「テンプレートメタプログラミング」を許可することもありません。
C ++テンプレートのもう1つの利点は、特殊化です。
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
これで、sumをポインターで呼び出すと2番目のメソッドが呼び出され、sumを非ポインターオブジェクトで呼び出すsum
と最初のメソッドが呼び出され、Special
オブジェクトで呼び出すと3番目のメソッドが呼び出されます。これはJavaでは可能だとは思いません。
@キース:
そのコードは実際には間違っており、小さな不具合(template
省略、特殊化構文の見た目が異なる)を除いて、部分的な特殊化は関数テンプレートでは機能せず、クラステンプレートでのみ機能します。ただし、コードは部分的なテンプレート特殊化なしで機能し、代わりにプレーンな古いオーバーロードを使用します。
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
以下の答えは、書籍 『クラッキングザコーディングインタビューソリューション』から第13章までです。
Javaジェネリックスの実装は、「型消去:」という考え方に基づいています。この手法では、ソースコードがJava仮想マシン(JVM)バイトコードに変換されるときに、パラメーター化された型を排除します。たとえば、次のJavaコードがあるとします。
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
コンパイル中に、このコードは次のように書き直されます。
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Javaジェネリックの使用は、私たちの機能についてそれほど大きな変化はありませんでした。それは物事を少しきれいにしただけです。このため、Javaジェネリックは「シンタックスシュガー」と呼ばれることもあります。
これはC ++とはかなり異なります。C ++では、テンプレートは本質的に栄光のあるマクロセットであり、コンパイラーは各タイプのテンプレートコードの新しいコピーを作成します。これの証明は、MyClassのインスタンスが静的変数をMyClassと共有しないことです。ただし、MyClassの2つのインスタンスは静的変数を共有します。
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
Javaでは、静的変数は、異なる型パラメーターに関係なく、MyClassのインスタンス間で共有されます。
JavaジェネリックとC ++テンプレートには、他にも多くの違いがあります。これらには以下が含まれます:
テンプレートはマクロシステムにすぎません。構文糖。それらは実際のコンパイルの前に完全に拡張されます(または、少なくとも、コンパイラはあたかもそうだったかのように動作します)。
例:
2つの関数が必要だとします。1つの関数は、数値の2つのシーケンス(リスト、配列、ベクトルなど)を取り、それらの内積を返します。別の関数は、長さを受け取り、その長さの2つのシーケンスを生成し、それらを最初の関数に渡し、その結果を返します。キャッチは、2番目の関数を間違える可能性があるため、これら2つの関数は実際には同じ長さではないということです。この場合は、コンパイラーに警告する必要があります。プログラムが実行されているときではなく、コンパイルされているとき。
Javaでは、次のようなことができます。
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
C#では、ほとんど同じことを書くことができます。テンプレートをC ++で書き直そうとすると、テンプレートが無限に拡張されてしまうため、コンパイルできません。
ここでaskanydifferenceを引用したいと思います:
C ++とJavaの主な違いは、プラットフォームへの依存性にあります。C ++はプラットフォームに依存する言語ですが、Javaはプラットフォームに依存しない言語です。
上記のステートメントは、C ++が真のジェネリック型を提供できる理由です。Javaには厳密なチェックがあるため、C ++で許可されている方法でジェネリックを使用することはできません。