なぜJavaの汎用型は「偽汎用型」なのか.
11053 ワード
個人ブログ:https://blog.N0tExpectErr0r.cn
小欄:https://xiaozhuanlan.com/N0tExpectErr0r
Java汎用
Javaの汎用性はJDK 5がもたらす新しい特性であり、次のような利点があります.は、同じコード を実行するために複数のデータ型に適用される.汎用のタイプ使用時に を指定する.汎用型は結局「模版」 しかし、下向き互換性を実現するために、Javaの汎用型は文法糖にすぎず、C++のような真の汎用型ではない.
どのように証明しますか?次の例を見てみましょう
アテステーション
この例では、
次のようにaddメソッドを呼び出して
エラーメッセージは次のとおりです.
add (java.lang.Integer) in List cannot be applied to (java.lang.String)
明らかに、Stringタイプの値を直接加えることはできません.
しかし、私たちは別の道を切り開くことができます.反射によりadd法を間接的に呼び出し,この
実行中にエラーは発生せず、strの印刷に成功しました!
[1, str] str
このことから,汎型とは確かに偽汎型であることがわかる.もともとIntegerのリストにしか読み込めなかったが、Stringタイプの値を読み込むことに成功した.
タイプ消去
実際,Javaの汎用はコンパイル期間のみ有効であり,実行期間では消去され,つまりすべての汎用パラメータタイプはコンパイル後に消去される.これがいわゆるタイプ消去です.
次の例を示します.
このクラスでは,
しかし、コンパイラは次のエラーを報告します.
Method addList(List) has the same erasure addList(List) as another method in type Test
すなわち,この2つの方法の署名は,タイプ消去後ともに
以下に、リストの種類消去について説明します.
C++テンプレート
C++のテンプレートは、我々のいわゆる「真汎用型」であり、以下に、C++がテンプレートを使用するクラスの例を示します.
クラスTestにはタイプTのオブジェクトが格納されている.興味深いことに、
Testのコンストラクション関数を呼び出してこのテンプレートをインスタンス化すると,コンパイラが調べたところ,Aクラスにはfunメソッドが含まれていることが分かったので,コンパイルは通過できる.
Aクラスにfunメソッドが含まれていない場合、コンパイルは通過しません.このようなタイプの安全が保障されています.
C++テンプレートでは、コンパイラは提供されたタイプのパラメータを使用してテンプレートを拡張するので、
まとめ
Javaの汎用型は「偽」ですが、消去のタイプがあります.しかし,汎用的な導入がJava言語に与える影響は依然として大きいことは否めない.
そのため、汎用型を使用する場合は、できるだけタイプ消去という特徴を考慮し、自分のパッケージがタイプ安全かどうかを考えなければならない.
小欄:https://xiaozhuanlan.com/N0tExpectErr0r
Java汎用
Javaの汎用性はJDK 5がもたらす新しい特性であり、次のような利点があります.
どのように証明しますか?次の例を見てみましょう
アテステーション
この例では、
List
のセットを定義し、addメソッドを呼び出してInteger
タイプの値を加えることができる.次のようにaddメソッドを呼び出して
String
を追加すると、当然エラーが発生します.public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add("str");
}
エラーメッセージは次のとおりです.
add (java.lang.Integer) in List cannot be applied to (java.lang.String)
明らかに、Stringタイプの値を直接加えることはできません.
しかし、私たちは別の道を切り開くことができます.反射によりadd法を間接的に呼び出し,この
List
にString
型の値を加えることを試みた.public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
Method add = list.getClass().getMethod("add", Object.class);
add.invoke(list,"str");
System.out.println(list);
System.out.println(list.get(1));
}
実行中にエラーは発生せず、strの印刷に成功しました!
[1, str] str
このことから,汎型とは確かに偽汎型であることがわかる.もともとIntegerのリストにしか読み込めなかったが、Stringタイプの値を読み込むことに成功した.
タイプ消去
実際,Javaの汎用はコンパイル期間のみ有効であり,実行期間では消去され,つまりすべての汎用パラメータタイプはコンパイル後に消去される.これがいわゆるタイプ消去です.
次の例を示します.
public class Test {
public void addList(List<String> stringList){
}
public void addList(List<Integer> intList) {
}
}
このクラスでは,
List
とList
の2つの異なるタイプのパラメータを入力する2つの方法を定義し,問題はないように見えた.しかし、コンパイラは次のエラーを報告します.
Method addList(List) has the same erasure addList(List) as another method in type Test
すなわち,この2つの方法の署名は,タイプ消去後ともに
addList(List)
であった.以下に、リストの種類消去について説明します.
List
、List
消去後のタイプはList
である.List[]
、List[]
消去後のタイプはList[]
である.List extends E>
、List super E>
消去後のタイプはList
である.List
消去後のタイプはList
です.C++テンプレート
C++のテンプレートは、我々のいわゆる「真汎用型」であり、以下に、C++がテンプレートを使用するクラスの例を示します.
#include
using namespace std;
template<class T> class Test
{
private:
T obj;
public:
Test(T x){obj=x;}
void try(){obj.fun();}
};
class A
{
public:
void fun(){cout<<"A::fun()"<<endl;}
};
int main(int argc, char* argv[]){
A a;
Test<A> test(a);
test.try();
}
クラスTestにはタイプTのオブジェクトが格納されている.興味深いことに、
Test::try()
メソッドでobj
メソッドを呼び出した.fun()
の方法がTに含まれていることをどうして知っていますか.Testのコンストラクション関数を呼び出してこのテンプレートをインスタンス化すると,コンパイラが調べたところ,Aクラスにはfunメソッドが含まれていることが分かったので,コンパイルは通過できる.
Aクラスにfunメソッドが含まれていない場合、コンパイルは通過しません.このようなタイプの安全が保障されています.
C++テンプレートでは、コンパイラは提供されたタイプのパラメータを使用してテンプレートを拡張するので、
fun()
のために生成されたC++コードはList
のために生成されたコードとは異なり、List
とList
は実際には2つの異なるクラスである.まとめ
Javaの汎用型は「偽」ですが、消去のタイプがあります.しかし,汎用的な導入がJava言語に与える影響は依然として大きいことは否めない.
そのため、汎用型を使用する場合は、できるだけタイプ消去という特徴を考慮し、自分のパッケージがタイプ安全かどうかを考えなければならない.