ジェネリックパラメーターの型を取得することは可能ですか?
例:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
一つの構造、私はかつてのようにつまずいた
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
そのため、私は不幸にも完全には理解していないいくつかのリフレクションマジックがあるようです...ごめんなさい。
私は@DerMikeからの答えを分解して説明したい:
まず、型消去は、実行時にJDK eliminates型情報を意味するものではありません。これは、コンパイル時の型チェックとランタイム型の互換性を同じ言語で共存させるための方法です。このコードブロックが示すように、JDKは消去された型情報を保持します。これは、チェックされたキャストなどに関連付けられていないだけです。
第二に、これは、ジェネリック型情報をジェネリッククラスに、チェックされている具象型からちょうど1レベル上の階層に提供します。ジェネリック型パラメーターを持つ抽象親クラスは、直接が継承するそれ自体の具象実装の型パラメーターに対応する具象型を見つけることができます。このクラスが抽象的でなく、インスタンス化されている場合、または具体的な実装が2レベル下の場合、これは機能しません(わずかな混乱が1つを超える、または最下位のクラスまでの事前に決められた数のレベルに適用される可能性がありますが) Xジェネリック型パラメーターなどを使用)。
とにかく、説明に。参照しやすいように、次のコードを再び行に分けて示します。
1#クラスgenericParameter0OfThisClass = 2#(クラス) 3#((ParameterizedType) 4#getClass() 5#.getGenericSuperclass ()) 6#.getActualTypeArguments()[0];
このコードを含むジェネリック型を持つ抽象クラスを「us」とします。これを大まかに裏返す:
...それでおしまいです。したがって、型情報を独自の具体的な実装から自分自身にプッシュし、それを使用してクラスハンドルにアクセスします。 getGenericSuperclass()を2倍にして2つのレベルに移動するか、getGenericSuperclass()を削除して具体的な型として自分自身の値を取得することができます(注意:これらのシナリオはまだテストしていません。
具体的な子が任意のホップ数離れている場合、または具体的で最終的なものではない場合、特に(さまざまな深さの)子に独自のジェネリックがあると予想される場合は特に注意が必要です。ただし、通常はこれらの考慮事項に基づいて設計することができるため、ほとんどの方法でこれが可能になります。
これが誰かを助けたことを願っています!この投稿は古代のものだと思います。私はおそらくこの説明を切り抜いて、他の質問のために保管します。
実際、私はこれを機能させました。次のスニペットを検討してください。
Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains Java.lang.String for method like "method(List<String> value)"
}
}
私はjdk 1.6を使用しています
"匿名クラス"トリック と Super Type Tokens からのアイデアを適用することにより、実際に解決策があります。
public final class Voodoo {
public static void chill(final List<?> aListWithSomeType) {
// Here I'd like to get the Class-Object 'SpiderMan'
System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
System.out.println(((ParameterizedType) aListWithSomeType
.getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>() {});
}
}
class SpiderMan {
}
トリックは、元の(単純な)new ArrayList<SpiderMan>() {}
の代わりに、anonymousクラス、new ArrayList<SpiderMan>()
を作成することにあります。 (可能な場合)匿名クラスを使用すると、型パラメーターList<?>
に指定された型引数SpiderMan
に関する情報がコンパイラーによって保持されます。ほら!
タイプの消去のため、リストのタイプを知る唯一の方法は、メソッドにパラメーターとしてタイプを渡すことです。
public class Main {
public static void main(String[] args) {
doStuff(new LinkedList<String>(), String.class);
}
public static <E> void doStuff(List<E> list, Class<E> clazz) {
}
}
パラメータ化されたインターフェイスのジェネリックパラメータを取得するための@DerMikeの回答の付録(重複を避けるためにJava-8のデフォルトメソッド内で#getGenericInterfaces()メソッドを使用):
import Java.lang.reflect.ParameterizedType;
public class ParametrizedStuff {
@SuppressWarnings("unchecked")
interface Awesomable<T> {
default Class<T> parameterizedType() {
return (Class<T>) ((ParameterizedType)
this.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0];
}
}
static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};
public static void main(String[] args) {
System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
// --> Type is: ParameterizedStuff$Beer
}
いいえ、それは不可能です。下位互換性の問題のため、Javaのジェネリックは type erasure に基づいています。実行時には、非ジェネリックList
オブジェクトのみがあります。実行時の型パラメーターに関する情報はいくつかありますが、オブジェクトインスタンスではなく、クラス定義に存在します(つまり、「 このフィールドの定義はどの汎用タイプを使用しますか? 」と尋ねることができます)。
@bertolamiが指摘したように、変数型を使用して将来の値(typeOfList変数の内容)を取得することはできません。
それでも、次のようにクラスをパラメーターとして渡すことができます。
public final class voodoo {
public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = clazz;
}
public static void main(String... args) {
chill(new List<SpiderMan>(), Spiderman.class );
}
}
これは、クラス変数を ActivityInstrumentationTestCase2 のコンストラクターに渡す必要がある場合にGoogleが行うこととほぼ同じです。
Javaジェネリック型消去のため、Questionという答えは簡単ではありません。
より長い答えは、次のようなリストを作成した場合です。
new ArrayList<SpideMan>(){}
この場合、ジェネリック型は上記の新しい匿名クラスのジェネリックスーパークラスに保持されます。
リストでこれを行うことをお勧めするわけではありませんが、リスナーの実装です:
new Listener<Type>() { public void doSomething(Type t){...}}
また、スーパークラスとスーパーインターフェースのジェネリックタイプを外挿することはJVM間で変化するため、ジェネリックソリューションはいくつかの答えが示唆するほど簡単ではありません。
Iterable<?...>
を受け入れるか返すことを期待するメソッド用にこれをコーディングしました。コードは次のとおりです。
/**
* Assuming the given method returns or takes an Iterable<T>, this determines the type T.
* T may or may not extend WindupVertexFrame.
*/
private static Class typeOfIterable(Method method, boolean setter)
{
Type type;
if (setter) {
Type[] types = method.getGenericParameterTypes();
// The first parameter to the method expected to be Iterable<...> .
if (types.length == 0)
throw new IllegalArgumentException("Given method has 0 params: " + method);
type = types[0];
}
else {
type = method.getGenericReturnType();
}
// Now get the parametrized type of the generic.
if (!(type instanceof ParameterizedType))
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
ParameterizedType pType = (ParameterizedType) type;
final Type[] actualArgs = pType.getActualTypeArguments();
if (actualArgs.length == 0)
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
Type t = actualArgs[0];
if (t instanceof Class)
return (Class<?>) t;
if (t instanceof TypeVariable){
TypeVariable tv = (TypeVariable) actualArgs[0];
AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
return (Class) tv.getAnnotatedBounds()[0].getType();
}
throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}
Javaのジェネリックはコンパイル時にのみ考慮されるため、これは不可能です。したがって、Javaジェネリックはある種のプリプロセッサです。ただし、リストのメンバーの実際のクラスを取得できます。
変数からジェネリックパラメーターを取得することはできません。ただし、メソッドまたはフィールドの宣言からはできます。
Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
私が見つけたこの例のように、リフレクションを使用して汎用パラメーターのタイプを取得できます here :
import Java.lang.reflect.ParameterizedType;
import Java.lang.reflect.Type;
public class Home<E> {
@SuppressWarnings ("unchecked")
public Class<E> getTypeParameterClass(){
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
return (Class<E>) paramType.getActualTypeArguments()[0];
}
private static class StringHome extends Home<String>{}
private static class StringBuilderHome extends Home<StringBuilder>{}
private static class StringBufferHome extends Home<StringBuffer>{}
/**
* This prints "String", "StringBuilder" and "StringBuffer"
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object object0 = new StringHome().getTypeParameterClass().newInstance();
Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
System.out.println(object0.getClass().getSimpleName());
System.out.println(object1.getClass().getSimpleName());
System.out.println(object2.getClass().getSimpleName());
}
}
このコードスニペットを読むのは大変でしたが、読みやすい2行に分割しました。
// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
メソッドにパラメーターを持たずに、Typeパラメーターのインスタンスを作成したかったのです。
publc T getNewTypeInstance(){
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
// for me i wanted to get the type to create an instance
// from the no-args default constructor
T t = null;
try{
t = c.newInstance();
}catch(Exception e){
// no default constructor available
}
return t;
}