Answers:
我曾经偶然发现的一种构造看起来像
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保留了已擦除的类型信息-只是与已检查的强制类型转换和内容无关。
其次,这将泛型类型信息提供给泛型类,该泛型类恰好位于要检查的具体类型的层次结构之上,即具有泛型类型参数的抽象父类可以找到与其类型参数相对应的具体类型,以实现自身的具体实现即直接从它继承。如果这个类不是抽象的并且被实例化的,或者具体的实现是降低了两个级别,那么这是行不通的(尽管有些麻烦可以使它应用于除一个级别之外的任何预定数量的级别,或者适用于最低级别的级别。带有X个通用类型参数等)。
无论如何,继续解释。这是再次的代码,为了便于参考,将其分成几行:
1#类genericParameter0OfThisClass = 2#(班级) 3#((参数化类型) 4#getClass() 5#.getGenericSuperclass()) 6#.getActualTypeArguments()[0];
假设“我们”是包含此代码的泛型类型的抽象类。从内到外大致阅读:
...就是这样。因此,我们将类型信息从我们自己的具体实现中推回我们自己,并使用它来访问类句柄。我们可以将getGenericSuperclass()加倍,并分为两个级别,或者消除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>() {}
在原有的(简单)的地方new ArrayList<SpiderMan>()
。使用匿名类(如果可能的话)确保编译器保留有关SpiderMan
赋予type参数的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的泛型基于类型Erase,例如在运行时,您拥有的只是一个非泛型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或多或少会这样做。
您可以通过反射来获得泛型参数的类型,例如在此示例中(我在这里找到):
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的泛型仅在编译时考虑。因此,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();
就我而言,阅读这段代码非常困难,我将其分为两行:
// 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;
}
这是另一个技巧。使用通用vararg数组
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
。
如果您需要一个不会失败的可靠解决方案,我可以建议两个。
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;
}
}