回答:
1つの構造、私はかつてのように偶然出会った
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
だから、残念ながら私は完全には理解できないいくつかの反射魔法があるようです...すみません。
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
制約が何であるかわかりません。
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
私は@DerMikeからの答えを分解して説明したいと思います:
まず、型の消去は、JDKが実行時に型情報を削除することを意味しません。これは、コンパイル時の型チェックとランタイム型の互換性を同じ言語で共存させるための方法です。このコードブロックが示すように、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を使用しています
「匿名クラス」のトリックとスーパータイプトークンのアイデアを適用することで、実際に解決策があります。
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>() {}
元の(簡単な)の代わりに、new ArrayList<SpiderMan>()
。匿名クラス(可能な場合)を使用すると、コンパイラーがSpiderMan
typeパラメーターに指定された型引数に関する情報を確実に保持しますList<?>
。ほら!
型の消去のため、リストの型を知る唯一の方法は、型をパラメーターとしてメソッドに渡すことです。
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
}
Awesomeable<Beer>
。その場合、タイプ情報は保持されます。new Awesomable<Beer> ()
メソッドに渡すとwtは機能しません。
Awesomable<Beer>
ようEstrellaGalicia
に具象サブクラスの明示的な定義なしでオンザフライで渡すと、それでもパラメーター化されたタイプが取得されます。今すぐ実行しました:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> タイプは次のとおりです:ParameterizedStuff $ Beer
いいえ、それは不可能です。下位互換性の問題により、Javaのジェネリックは型の消去に基づいてい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でのジェネリックのタイプを知るを参照してください。
あなたは私がここで見つけたこの例のようなリフレクションでジェネリックパラメーターのタイプを取得することができます:
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());
}
}
Javaのジェネリック型の消去のため、質問にすばやく答えることはできません。
より長い答えは、次のようにリストを作成した場合です。
new ArrayList<SpideMan>(){}
次に、この場合、ジェネリック型は上記の新しい匿名クラスのジェネリックスーパークラスに保持されます。
リストでこれを行うことをお勧めしますが、これはリスナーの実装です。
new Listener<Type>() { public void doSomething(Type t){...}}
また、JVM間でスーパークラスとスーパーインターフェースのジェネリック型を外挿すると、ジェネリックソリューションは、いくつかの回答が示唆するほど簡単ではありません。
Javaのジェネリックはコンパイル時にのみ考慮されるため、これは不可能です。したがって、Javaジェネリックは単なる一種のプリプロセッサです。ただし、リストのメンバーの実際のクラスを取得できます。
これを受け入れるか返すことを期待するメソッドのためにこれをコーディングしました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());
}
変数からジェネリックパラメーターを取得することはできません。ただし、メソッドまたはフィールドの宣言から行うことができます。
Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
私がこのコードスニペットを読むのは大変だったので、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;
}
ここに別のトリックがあります。汎用可変引数配列を使用する
import java.util.ArrayList;
class TypedArrayList<E> extends ArrayList<E>
{
@SafeVarargs
public TypedArrayList (E... typeInfo)
{
// Get generic type at runtime ...
System.out.println (typeInfo.getClass().getComponentType().getTypeName());
}
}
public class GenericTest
{
public static void main (String[] args)
{
// No need to supply the dummy argument
ArrayList<Integer> ar1 = new TypedArrayList<> ();
ArrayList<String> ar2 = new TypedArrayList<> ();
ArrayList<?> ar3 = new TypedArrayList<> ();
}
}
多くの人がgetGenericSuperclass()
解決策に傾いていることに気づきました:
class RootGeneric<T> {
public Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
ただし、このソリューションはエラーが発生しやすくなります。それはなりません子孫でジェネリック医薬品がある場合は正常に動作します。このことを考慮:
class Foo<S> extends RootGeneric<Integer> {}
class Bar extends Foo<Double> {}
どのタイプになりBar.persistentClass
ますか?Class<Integer>
?いいえ、そうなりますClass<Double>
。これは、getClass()
常に最上位のクラス(Bar
この場合)を返すために発生し、その総称スーパークラスはFoo<Double>
です。したがって、引数の型はになりますDouble
。
失敗しない信頼できるソリューションが必要な場合は、2つお勧めします。
Guava
ます。この目的のために作成されたクラスがありますcom.google.common.reflect.TypeToken
。それはすべてのコーナーケースをうまく処理し、いくつかのより良い機能を提供します。欠点は、追加の依存関係です。このクラスを使用した場合、コードは次のようにシンプルで明確になります。class RootGeneric<T> {
@SuppressWarnings("unchecked")
public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
abstract class RootGeneric<T> {
@SuppressWarnings("unchecked")
private Class<T> getTypeOfT() {
Class<T> type = null;
Class<?> iter = getClass();
while (iter.getSuperclass() != null) {
Class<?> next = iter.getSuperclass();
if (next != null && next.isAssignableFrom(RootGeneric.class)) {
type =
(Class<T>)
((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
break;
}
iter = next;
}
if (type == null) {
throw new ClassCastException("Cannot determine type of T");
}
return type;
}
}
使用する:
Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();
toArray()
を返しますObject[]
。特定のサブタイプであることに依存することはできません。