Java汎用および関連使用
3716 ワード
面接で問題が発生しました.汎用型はどのように本当のタイプを取得しますか?GG、降りてから関連資料を調べて、何回見てやっとどういう意味か分かりました.ここに書いて覚えてください.
汎用型
汎用:オブジェクトの作成または実行方法に明確なタイプを遅らせることです.汎用型はパラメータに相当し、このクラスを修飾するのは実際には何ですか.例えば、ArrayListではEは未定のパラメータであり、ArrayListではIntegerは決定された実際のパラメータに固定される.ArrayList全体を汎用タイプと呼び、ArrayListをパラメトリックタイプ(ParameterizedType)と呼ぶ.汎用パラメータTを含むクラスを定義し、クラスにT変数を設定すると、クラスで直接Objectクラスを定義するよりも、次のようなメリットがあります.この汎用クラスを作成すると、Tのタイプが固定され、エラーパラメータが入力されることはありません. がTを取得した場合,取得したのは実際のタイプであり,強転問題が発生しないようにもう一度強転を行う必要はない.
汎用クラスをインスタンス化する場合、<>指定タイプを使用しないか、>指定タイプを使用しない場合、汎用パラメータはObjectに相当し、汎用クラスは汎用の役割を失います.汎用クラスをインスタンス化するときに、>定義を使用すると、?条件extends Aまたはsuper Aを添付します.前者はそのときこのパラメトリックタイプ変数がAのサブクラスであるべきであることを示し,後者はそのときこのパラメトリックタイプ変数がBのスーパークラス(先祖クラス)であるべきであることを示す.
また、定義時には、入力したパラメータのタイプ範囲を縮小するために使用できます.
汎用消去
汎用クラスはコンパイル後、<>のタイプ情報が消去され、メモリで実際にObjectクラスとして表現されます.get()などの方法で正確なタイプを返す必要がある場合、実際の汎用パラメータタイプは直接書き込まれます.例えばArrayListは、コンパイル後、メモリにArrayListとしてのみ表示され、そのget()メソッドは
になる
ArrayListを例にとると,get()メソッドを呼び出して内部Object[]配列から元のデータを取得し,Integerに強転して返す.
汎用消去の概念がある以上,一つの表面定型,実は不定型のArrayListにとって,個々の要素を挿入することも可能である.反射してArrayListを取得するadd(Object o)メソッドを使用すると、他の要素を挿入できます.
汎用リアリスティックタイプの取得
汎用型はどのようにして実際のタイプを取得しますか?3つの状況に分けて分析します
1.あるクラスはある汎用タイプを継承している
2.あるクラスはある汎用インタフェースを実現した
ヒント:ここでは状況1と似ていますが、親は1つしかありません.インタフェースはたくさんあるので、getGenericSuperclass()はTypeを返し、getGenericInterfaces()はType[]配列を返します.
3.メソッドの戻りパラメータが汎用型である
この場合、頼りになる方法はなく、この汎用タイプが空でなく、値が統一されている(前述のような反射が他のタイプに書き込まれていない場合)ことを確保できれば、頼りにならない方法で得ることができる.コードは次のとおりです.
汎用型
汎用:オブジェクトの作成または実行方法に明確なタイプを遅らせることです.汎用型はパラメータに相当し、このクラスを修飾するのは実際には何ですか.例えば、ArrayListではEは未定のパラメータであり、ArrayListではIntegerは決定された実際のパラメータに固定される.ArrayList全体を汎用タイプと呼び、ArrayListをパラメトリックタイプ(ParameterizedType)と呼ぶ.汎用パラメータTを含むクラスを定義し、クラスにT変数を設定すると、クラスで直接Objectクラスを定義するよりも、次のようなメリットがあります.
汎用クラスをインスタンス化する場合、<>指定タイプを使用しないか、>指定タイプを使用しない場合、汎用パラメータはObjectに相当し、汎用クラスは汎用の役割を失います.汎用クラスをインスタンス化するときに、>定義を使用すると、?条件extends Aまたはsuper Aを添付します.前者はそのときこのパラメトリックタイプ変数がAのサブクラスであるべきであることを示し,後者はそのときこのパラメトリックタイプ変数がBのスーパークラス(先祖クラス)であるべきであることを示す.
また、定義時には、入力したパラメータのタイプ範囲を縮小するために使用できます.
汎用消去
汎用クラスはコンパイル後、<>のタイプ情報が消去され、メモリで実際にObjectクラスとして表現されます.get()などの方法で正確なタイプを返す必要がある場合、実際の汎用パラメータタイプは直接書き込まれます.例えばArrayListは、コンパイル後、メモリにArrayListとしてのみ表示され、そのget()メソッドは
E get(){/* ... */}
になる
Integer get(){/* ... */}
ArrayListを例にとると,get()メソッドを呼び出して内部Object[]配列から元のデータを取得し,Integerに強転して返す.
汎用消去の概念がある以上,一つの表面定型,実は不定型のArrayListにとって,個々の要素を挿入することも可能である.反射してArrayListを取得するadd(Object o)メソッドを使用すると、他の要素を挿入できます.
ArrayList ai = new ArrayList<>();
ai.add(123);
try {
ai.getClass().getDeclaredMethod("add",Object.class).invoke(ai,"string");
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
for (Object o:ai)
System.out.println(o);
for (Integer i:ai)
// ClassCastException, String Integer
System.out.println(i);
汎用リアリスティックタイプの取得
汎用型はどのようにして実際のタイプを取得しますか?3つの状況に分けて分析します
1.あるクラスはある汎用タイプを継承している
private static class Generic{
T data;
int id;
}
private static class ExtendedGeneric extends Generic{
private void te(){
}
}
public static void main(String[] args) throws Exception {
Type t= ((ParameterizedType) ExtendedGeneric.class.
getGenericSuperclass()).getActualTypeArguments()
[0];
System.out.println(t.getTypeName());
}
:java.lang.Integer
2.あるクラスはある汎用インタフェースを実現した
private interface GenericInterface{
P getP();
}
private static class ImplGeneric implements GenericInterface{
@Override
public Pipe getP() {
return null;
}
}
public static void main(String[] args) throws Exception {
Type t= ((ParameterizedType) ImplGeneric.class.
getGenericInterfaces()[0]).getActualTypeArguments()
[0];
System.out.println(t.getTypeName());
}
:java.nio.channels.Pipe
ヒント:ここでは状況1と似ていますが、親は1つしかありません.インタフェースはたくさんあるので、getGenericSuperclass()はTypeを返し、getGenericInterfaces()はType[]配列を返します.
3.メソッドの戻りパラメータが汎用型である
この場合、頼りになる方法はなく、この汎用タイプが空でなく、値が統一されている(前述のような反射が他のタイプに書き込まれていない場合)ことを確保できれば、頼りにならない方法で得ることができる.コードは次のとおりです.
public static void main(String[] args) throws Exception {
ArrayList strings = new ArrayList<>();
strings.add("123");
Class> c= ArrayList.class.getDeclaredMethod("get", int.class)
.invoke(strings,0).getClass();
System.out.println(c.getName());
}
:java.lang.String