2010-03-23 123 views
3

在我的实用方法JUnit测试:问题在泛型

public static <T> T getField(Object obj, Class c, String fieldName) { 
    try { 
     Field field = c.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     return (T) field.get(obj); 
    } catch (Exception e) { 
     e.printStackTrace(); 
     fail(); 
     return null; 
    } 
} 

线

return (T) field.get(obj); 

给出警告 “类型安全:未选中从对象转换为T”; 但我不能对类型参数T, 执行instanceof检查,所以我想在这里做什么?

回答

2

注释@SuppressWarnings将停止编译器报告此警告。我不认为有什么方法可以避免使用像这样的反射时的编译器警告。类似下面的内容:

Field field = c.getDeclaredField(fieldName); 
field.setAccessible(true); 

@SuppressWarnings(value="unchecked") 
T t = (T) field.get(obj); 

return t; 
+1

您可以指定'类'(非通用'T')在其他的答案中提到,但有ISN测试代码中没有任何一点。反射泛型只是一个助手 - 它们并不是真正正确的(参见'Object.getClass'),这并不重要,因为当你使用反射时,无论如何你都会吹走安全类型。 – 2010-03-23 15:56:07

0

泛型在那里提供类型安全的地方,你以前没有任何在Java中。因此,它使用的是,如果你有一个完整的字符串列表,你不得不这样做:

String myString = (String)myList.get(0); 

但现在你可以检索它没有铸造它:

String myString = myList.get(0); //Compiler won't complain 

当泛型化使用的变量T ,你说T是特定类型的占位符,它将在实例化时在类的实例上定义。例如:

public class ArrayList<T> { 
    public ArrayList<T> { 
    .... 
    } 
} 

允许您使用实例名单:

ArrayList<String> myList = new ArrayList<String>(); 

现在在ArrayList的每个函数会返回一个字符串,并且编译器知道这所以它并不需要强制。这些功能中的每定义就像上面你:

public T get(int index); 
    public void set(int index, T object); 

在编译时它们变为

public String get(int index); 
    public void set(int index, String object); 

在你的情况,但是,你似乎是试图用T作为通配符,与特定类型的占位符不同。你可以为三个不同的字段调用这个方法三次,每个字段都有不同的返回类型,对吧?这意味着,当你实例化这个类,你不能挑单一类型为T.

在一般情况下,看看你的方法签名,并问自己“将单一类型来代替T代表的这种每个实例类”?

public static <T> T getField(Object obj, Class c, String fieldName) 

如果答案是“否”,这意味着这不适合泛型。由于每次通话都会返回不同的类型,因此您必须从通话中投出结果。如果你把它放在这个函数中,你将失去泛型将会提供的任何好处,并且不妨让自己免于头疼。

如果我错误地理解了你的设计,并且T引用了单一类型,那么只需使用@SuppressWarnings(value =“unchecked”)注释该调用即可。但是,如果我理解正确,修复这个错误只会导致你走向漫长的混乱之路,除非你对上面所写的内容感兴趣。

祝你好运!

1

您可以轻松地通过增加一个额外的参数,以你的方法将指定申请的类型解决了这个问题,于是,该方法将如下所示:

public static <T> T getField(Class<T> fieldType, Object obj, Class<?> c, 
    String fieldName) 
    { 
    try { 
     Field field = c.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     Object value = field.get(obj); 
     return fieldType.cast(value); 
    } catch (Exception e) { 
     e.printStackTrace(); 
     fail(); 
     return null; 
    } 
} 

而且这里是你如何使用它: getField(String.class, new G(), G.class, "s")其中G的定义如下:

public class G { 
    String s = "abc";  
} 

甲第二改进是消除则getfiled的c-参数()。通过调用obj.getClass()可以在方法内部获得c。唯一需要注意的是,这会给你动态的对象类型,所以你可以循环遍历C的所有超类,直到找到你要找的字段,或者直到你到达Object(你还需要使用c.getFields()并查找结果数组中的字段)。

我认为这些更改会使您的方法更易于使用,并且不易出错,因此值得付出努力。

0

如上所示,您可以指定字段的预期类型并调用cast方法。

另外。你不需要传递参数对象的类。你可以发现它是通过调用obj.getClass()

什么这简化了你的代码

public static <T> T getField(Object obj, Class<T> fieldClass, String fieldName) { 
    try { 
     Class<?> declaringClass = obj.getClass(); 
     Field field = declaringClass.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     return fieldClass.cast(field.get(obj)); 
    } 
    catch (Exception e) { 
     throw new AssertionFailedError(); 
    } 
}