Last active
February 5, 2018 06:51
-
-
Save seraphy/c8f088fc081054116266011ae56d4bd7 to your computer and use it in GitHub Desktop.
Javaのジェネリック型引数をリフレクションによって取得する方法の実験サンプル。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package jp.seraphyware.example; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.GenericArrayType; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.ParameterizedType; | |
import java.lang.reflect.Type; | |
import java.lang.reflect.TypeVariable; | |
import java.lang.reflect.WildcardType; | |
import java.sql.Timestamp; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* ジェネリック型の定義 | |
* @param <E> | |
*/ | |
interface GenericIntf<E extends Date> { | |
void set(E value); | |
E get(); | |
} | |
/** | |
* ジェネリック型を継承した具象クラスの定義 | |
*/ | |
class ConcreteCls implements GenericIntf<Timestamp> { | |
private Timestamp value; | |
@Override | |
public void set(Timestamp v) { | |
this.value = v; | |
} | |
@Override | |
public Timestamp get() { | |
return value; | |
} | |
} | |
/** | |
* ジェネリック型を継承したジェネリッククラスの定義 | |
*/ | |
class GenericCls<E extends Date> implements GenericIntf<E> { | |
private E value; | |
@Override | |
public void set(E v) { | |
this.value = v; | |
} | |
@Override | |
public E get() { | |
return value; | |
} | |
} | |
/** | |
* ジェネリック型引数の実験対象 | |
*/ | |
class GenericExample { | |
/** | |
* リスト型を引数にとるメソッド例1 | |
* @param args | |
*/ | |
public static void method1(List<String> args) { | |
} | |
/** | |
* リスト型を引数にとるメソッド例2 | |
* @param args | |
*/ | |
public static void method2(List<List<GenericIntf<java.sql.Date>>> args) { | |
} | |
/** | |
* マップ型を戻り値にとるメソッド例 | |
* @param args | |
*/ | |
public static Map<String, GenericIntf<java.sql.Date>> method3() { | |
return Collections.emptyMap(); | |
} | |
/** | |
* ジェネリック配列を返すメソッド例1 | |
* @return | |
*/ | |
@SuppressWarnings("unchecked") | |
public static GenericIntf<Date>[] getGenericArray() { // メソッド定義の戻り値型の情報としてジェネリック配列型は有効である | |
return new GenericIntf[0]; // ただし、ジェネリック配列を直接作成できないのでRAW型配列として作成する. | |
} | |
/** | |
* ジェネリック配列を返すメソッド例2 | |
* @return | |
*/ | |
@SuppressWarnings("unchecked") | |
public static GenericIntf<Date>[][] getGenericArray2() { // メソッド定義の戻り値型の情報としてジェネリック配列型は有効である | |
return new GenericIntf[][]{}; // ただし、ジェネリック配列を直接作成できないのでRAW型配列として作成する. | |
} | |
/** | |
* ジェネリックメソッドの例 | |
* @return | |
*/ | |
public static <A extends Date & java.io.Serializable, B> B genericMethodExample(A arg) { | |
return null; | |
} | |
/** | |
* ワイルドカード型フィールドの例 | |
*/ | |
public static final List<? super Number> wildcardFieldSuper = new ArrayList<>(); | |
public static final List<? extends Number> wildcardFieldExtends = new ArrayList<>(); | |
} | |
/** | |
* リフレクションによるジェネリックの型引数の取得実験例 | |
*/ | |
public class GenericReflectionExample { | |
@Comment("ジェネリック型を匿名クラスとしてメソッド内で利用する例1") | |
private static void testGenericAnonymous1() { | |
System.out.println("☆" + new ThisMethod() {}); | |
GenericIntf<Date> obj = new GenericIntf<Date>() { // 匿名型の生成 | |
@Override | |
public void set(Date v) {} | |
@Override | |
public Date get() { | |
return null; | |
} | |
}; | |
System.out.println("target=" + obj.getClass().toGenericString()); | |
Type typ = obj.getClass().getGenericInterfaces()[0]; // 実装しているインターフェイスを取得 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック型を匿名クラスとしてメソッド内で利用する例2") | |
private static void testGenericAnonymous2() { | |
System.out.println("☆" + new ThisMethod() {}); | |
GenericCls<Date> obj = new GenericCls<Date>() {}; // 匿名クラス化する | |
System.out.println("target=" + obj.getClass().toGenericString()); | |
Type typ = obj.getClass().getGenericSuperclass(); // 匿名クラスの親クラスを取得 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック派生型をメソッド内で利用する例1") | |
private static void testGenericUse1() { | |
System.out.println("☆" + new ThisMethod() {}); | |
ConcreteCls obj = new ConcreteCls(); | |
System.out.println("target=" + obj.getClass().toGenericString()); | |
Type typ = obj.getClass().getGenericInterfaces()[0]; // 実装しているインターフェイスを取得 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック派生型をメソッド内で利用する例2") | |
private static void testGenericUse2() { | |
System.out.println("☆" + new ThisMethod() {}); | |
GenericCls<java.sql.Date> obj = new GenericCls<>(); | |
System.out.println("target=" + obj.getClass().toGenericString()); | |
Type typ = obj.getClass().getGenericInterfaces()[0]; // 実装しているインターフェイスを取得 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック型を引数にとるメソッドの例1") | |
private static void testGenericArgument1() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("method1", List.class); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericParameterTypes()[0]; // 引数の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック型を引数にとるメソッドの例2") | |
private static void testGenericArgument2() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("method2", List.class); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericParameterTypes()[0]; // 引数の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック型を戻り値にとるメソッドの例") | |
private static void testGenericReturnType1() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("method3"); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericReturnType(); // 戻り値の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック型を戻り値にとるメソッドの例2") | |
private static void testGenericReturnType2() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
GenericCls<java.sql.Date> obj = new GenericCls<>(); | |
Method method = obj.getClass().getMethod("get"); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericReturnType(); // 戻り値の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック配列を戻り値にとるメソッドの例1") | |
private static void testGenericArray() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("getGenericArray"); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericReturnType(); // 戻り値の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリック配列の配列を戻り値にとるメソッドの例2") | |
private static void testGenericArray2() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("getGenericArray2"); | |
System.out.println("target=" + method.toGenericString()); | |
Type typ = method.getGenericReturnType(); // 戻り値の型 | |
showType(typ); | |
System.out.println(); | |
} | |
@Comment("ジェネリックメソッドの例") | |
private static void testGenericMethod() throws NoSuchMethodException, SecurityException { | |
System.out.println("☆" + new ThisMethod() {}); | |
Method method = GenericExample.class.getMethod("genericMethodExample", Date.class); | |
System.out.println("target=" + method.toGenericString()); | |
{ | |
Type typ = method.getGenericParameterTypes()[0]; // 戻り値の型 | |
showType(typ); | |
} | |
{ | |
// メソッドに付与されているパラメータ | |
Type[] typs = method.getTypeParameters(); | |
for (Type typ : typs) { | |
System.out.println("*メソッドの型引数: " + typ); | |
showType(typ); | |
} | |
} | |
System.out.println(); | |
} | |
@Comment("ワイルドカード型のフィールドの例") | |
private static void testWildcard() throws SecurityException, NoSuchFieldException { | |
System.out.println("☆" + new ThisMethod() {}); | |
for (String name : new String[]{"wildcardFieldSuper", "wildcardFieldExtends"}) { | |
Field field = GenericExample.class.getField(name); | |
System.out.println("target=" + field.toGenericString()); | |
Type typ = field.getGenericType(); | |
showType(typ); | |
} | |
System.out.println(); | |
} | |
/** | |
* エントリポイント | |
* @param args | |
* @throws Exception | |
*/ | |
public static void main(String[] args) throws Exception { | |
testGenericAnonymous1(); | |
testGenericAnonymous2(); | |
testGenericUse1(); | |
testGenericUse2(); | |
testGenericArgument1(); | |
testGenericArgument2(); | |
testGenericReturnType1(); | |
testGenericReturnType2(); | |
testGenericArray(); | |
testGenericArray2(); | |
testGenericMethod(); | |
testWildcard(); | |
} | |
/** | |
* 型情報を表示する | |
* 型情報がさらにネストした型情報をもっている場合は再帰的に掘り進む. | |
* @param typ | |
*/ | |
private static void showType(Type typ) { | |
showType(typ, ""); | |
} | |
/** | |
* 型情報を表示する. | |
* 型情報がさらにネストした型情報をもっている場合は再帰的に掘り進む. | |
* @param typ 型 | |
* @param prefix 印字する文字列のまえに付与する文字列 | |
*/ | |
private static void showType(Type typ, String prefix) { | |
System.out.print(prefix + "typ=" + typ); | |
String typeName; | |
if (typ instanceof Class) { | |
typeName = "Class"; // クラス | |
} else if (typ instanceof TypeVariable) { | |
typeName = "TypeVariable"; // 型変数型 | |
} else if (typ instanceof ParameterizedType) { | |
typeName = "ParameterizedType"; // ジェネリック型 | |
} else if (typ instanceof GenericArrayType) { | |
typeName = "GenericArrayType"; // ジェネリック配列型 | |
} else if (typ instanceof WildcardType) { | |
typeName = "WildcardType"; // ワイルドカード型 | |
} else { | |
// 不明? (java9時点では、上記5種のみのはず) | |
typeName = typ.getClass().getName(); | |
} | |
System.out.println(" -- " + typeName); | |
if (typ instanceof ParameterizedType) { | |
// ジェネリック型の場合は、その型引数を取得する | |
Type[] genericArgs = ((ParameterizedType) typ).getActualTypeArguments(); // 実際の型引数の取得 | |
for (int idx = 0; idx < genericArgs.length; idx++) { | |
showType(genericArgs[idx], prefix + "(" + (idx + 1) + ") "); | |
} | |
} else if (typ instanceof GenericArrayType) { | |
// ジェネリック配列の場合は、その要素型を取得する. | |
Type compType = ((GenericArrayType) typ).getGenericComponentType(); // 要素型の取得 | |
showType(compType, prefix + "(elm) "); | |
} else if (typ instanceof TypeVariable) { | |
// 型変数型の場合は型の制約(範囲)を取得する | |
Type[] bounds = ((TypeVariable<?>) typ).getBounds(); // 範囲の取得 | |
for (int idx = 0; idx < bounds.length; idx++) { | |
showType(bounds[idx], prefix + "(bound#" + (idx + 1) + ") "); | |
} | |
} else if (typ instanceof WildcardType) { | |
// ワイルドカード型の場合は型の制約(上限・下限)を取得する. | |
Type[] ups = ((WildcardType) typ).getUpperBounds(); // 範囲(上限)の取得 | |
for (int idx = 0; idx < ups.length; idx++) { | |
showType(ups[idx], prefix + "(upper#" + (idx + 1) + ") "); | |
} | |
Type[] lows = ((WildcardType) typ).getLowerBounds(); // 範囲(下限)の取得 | |
for (int idx = 0; idx < lows.length; idx++) { | |
showType(lows[idx], prefix + "(lower#" + (idx + 1) + ") "); | |
} | |
} | |
} | |
} | |
/** | |
* この抽象クラスをメソッド内で匿名クラス化した場合、 | |
* その匿名クラス化したメソッドへの情報にアクセスできるようにするためのヘルパ. | |
*/ | |
abstract class ThisMethod { | |
/** | |
* この抽象クラスを匿名クラス化したメソッドを取得する. | |
* @return メソッド | |
*/ | |
public Method getMethod() { | |
return getClass().getEnclosingMethod(); | |
} | |
/** | |
* この抽象クラスを匿名クラス化したメソッドがCommentアノテーションをもっている場合、そのコメントを取得する. | |
* なければnull | |
* @return | |
*/ | |
public String getComment() { | |
Comment comment = getMethod().getAnnotation(Comment.class); | |
return comment != null ? comment.value() : null; | |
} | |
/** | |
* この抽象クラスを匿名クラス化したメソッドのメソッド名とコメントをプリントする. | |
*/ | |
public String toString() { | |
StringBuilder buf = new StringBuilder(); | |
buf.append(getMethod().getName()); | |
String comment = getComment(); | |
if (comment != null) { | |
buf.append("(" + comment + ")"); | |
} | |
return buf.toString(); | |
} | |
} | |
/** | |
* メソッドにコメントを付与するアノテーション | |
*/ | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target({ElementType.METHOD, ElementType.TYPE}) | |
@interface Comment { | |
String value(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
☆testGenericAnonymous1(ジェネリック型を匿名クラスとしてメソッド内で利用する例1) | |
target=class jp.seraphyware.example.GenericReflectionExample$2 | |
typ=jp.seraphyware.example.GenericIntf<java.util.Date> -- ParameterizedType | |
(1) typ=class java.util.Date -- Class | |
☆testGenericAnonymous2(ジェネリック型を匿名クラスとしてメソッド内で利用する例2) | |
target=class jp.seraphyware.example.GenericReflectionExample$4 | |
typ=jp.seraphyware.example.GenericCls<java.util.Date> -- ParameterizedType | |
(1) typ=class java.util.Date -- Class | |
☆testGenericUse1(ジェネリック派生型をメソッド内で利用する例1) | |
target=class jp.seraphyware.example.ConcreteCls | |
typ=jp.seraphyware.example.GenericIntf<java.sql.Timestamp> -- ParameterizedType | |
(1) typ=class java.sql.Timestamp -- Class | |
☆testGenericUse2(ジェネリック派生型をメソッド内で利用する例2) | |
target=class jp.seraphyware.example.GenericCls<E> | |
typ=jp.seraphyware.example.GenericIntf<E> -- ParameterizedType | |
(1) typ=E -- TypeVariable | |
(1) (bound#1) typ=class java.util.Date -- Class | |
☆testGenericArgument1(ジェネリック型を引数にとるメソッドの例1) | |
target=public static void jp.seraphyware.example.GenericExample.method1(java.util.List<java.lang.String>) | |
typ=java.util.List<java.lang.String> -- ParameterizedType | |
(1) typ=class java.lang.String -- Class | |
☆testGenericArgument2(ジェネリック型を引数にとるメソッドの例2) | |
target=public static void jp.seraphyware.example.GenericExample.method2(java.util.List<java.util.List<jp.seraphyware.example.GenericIntf<java.sql.Date>>>) | |
typ=java.util.List<java.util.List<jp.seraphyware.example.GenericIntf<java.sql.Date>>> -- ParameterizedType | |
(1) typ=java.util.List<jp.seraphyware.example.GenericIntf<java.sql.Date>> -- ParameterizedType | |
(1) (1) typ=jp.seraphyware.example.GenericIntf<java.sql.Date> -- ParameterizedType | |
(1) (1) (1) typ=class java.sql.Date -- Class | |
☆testGenericReturnType1(ジェネリック型を戻り値にとるメソッドの例) | |
target=public static java.util.Map<java.lang.String, jp.seraphyware.example.GenericIntf<java.sql.Date>> jp.seraphyware.example.GenericExample.method3() | |
typ=java.util.Map<java.lang.String, jp.seraphyware.example.GenericIntf<java.sql.Date>> -- ParameterizedType | |
(1) typ=class java.lang.String -- Class | |
(2) typ=jp.seraphyware.example.GenericIntf<java.sql.Date> -- ParameterizedType | |
(2) (1) typ=class java.sql.Date -- Class | |
☆testGenericReturnType2(ジェネリック型を戻り値にとるメソッドの例2) | |
target=public E jp.seraphyware.example.GenericCls.get() | |
typ=E -- TypeVariable | |
(bound#1) typ=class java.util.Date -- Class | |
☆testGenericArray(ジェネリック配列を戻り値にとるメソッドの例1) | |
target=public static jp.seraphyware.example.GenericIntf<java.util.Date>[] jp.seraphyware.example.GenericExample.getGenericArray() | |
typ=jp.seraphyware.example.GenericIntf<java.util.Date>[] -- GenericArrayType | |
(elm) typ=jp.seraphyware.example.GenericIntf<java.util.Date> -- ParameterizedType | |
(elm) (1) typ=class java.util.Date -- Class | |
☆testGenericArray2(ジェネリック配列の配列を戻り値にとるメソッドの例2) | |
target=public static jp.seraphyware.example.GenericIntf<java.util.Date>[][] jp.seraphyware.example.GenericExample.getGenericArray2() | |
typ=jp.seraphyware.example.GenericIntf<java.util.Date>[][] -- GenericArrayType | |
(elm) typ=jp.seraphyware.example.GenericIntf<java.util.Date>[] -- GenericArrayType | |
(elm) (elm) typ=jp.seraphyware.example.GenericIntf<java.util.Date> -- ParameterizedType | |
(elm) (elm) (1) typ=class java.util.Date -- Class | |
☆testGenericMethod(ジェネリックメソッドの例) | |
target=public static <A,B> B jp.seraphyware.example.GenericExample.genericMethodExample(A) | |
typ=A -- TypeVariable | |
(bound#1) typ=class java.util.Date -- Class | |
(bound#2) typ=interface java.io.Serializable -- Class | |
*メソッドの型引数: A | |
typ=A -- TypeVariable | |
(bound#1) typ=class java.util.Date -- Class | |
(bound#2) typ=interface java.io.Serializable -- Class | |
*メソッドの型引数: B | |
typ=B -- TypeVariable | |
(bound#1) typ=class java.lang.Object -- Class | |
☆testWildcard(ワイルドカード型のフィールドの例) | |
target=public static final java.util.List<? super java.lang.Number> jp.seraphyware.example.GenericExample.wildcardFieldSuper | |
typ=java.util.List<? super java.lang.Number> -- ParameterizedType | |
(1) typ=? super java.lang.Number -- WildcardType | |
(1) (upper#1) typ=class java.lang.Object -- Class | |
(1) (lower#1) typ=class java.lang.Number -- Class | |
target=public static final java.util.List<? extends java.lang.Number> jp.seraphyware.example.GenericExample.wildcardFieldExtends | |
typ=java.util.List<? extends java.lang.Number> -- ParameterizedType | |
(1) typ=? extends java.lang.Number -- WildcardType | |
(1) (upper#1) typ=class java.lang.Number -- Class | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment