使用泛型进行 JUnit 测试时出现问题

发布于 2024-08-26 12:52:56 字数 496 浏览 4 评论 0原文

在我的实用方法中:

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 检查, 那么我应该在这里做什么呢?

In my utility method:

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;
    }
}

The line

return (T) field.get(obj);

gives the warning "Type safety: Unchecked cast from Object to T";
but I cannot perform instanceof check against type parameter T,
so what am I suppose to do here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

拥醉 2024-09-02 12:52:56

注释 @SuppressWarnings 将阻止编译器报告此警告。我认为在使用这样的反射时没有任何方法可以摆脱编译器警告。像下面这样:

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

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

return t;

The annotation @SuppressWarnings will stop the compiler reporting this warning. I don't think there's any way you can get away from the compiler warning when using reflection like this. Something like the following:

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

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

return t;
镜花水月 2024-09-02 12:52:56

您可以通过向方法添加一个附加参数来轻松解决此问题,该参数将指定字段的类型,该方法将如下所示:

  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() 并在结果数组中查找字段)。

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

You can easily solve this problem by adding an additional parameter to your method which will specify the type of the filed, the method will then look as follows:

  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;
     }
 }

And here's how you can use it: getField(String.class, new G(), G.class, "s") where G is defined as:

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

A 2nd improvement is to eliminate the c parameter of getFiled(). c can be obtained inside the method by invoking obj.getClass(). The only caveat is that this will give you the dynamic type of the object so you mat want to loop over all of C's superclasses until you find the field you're looking for, or until you arrive at Object (You will also need to use c.getFields() and look for the field in the resulting array).

I think that these changes will make your method easier to use and less prone to errors so it's worth the effort.

素衣风尘叹 2024-09-02 12:52:56

泛型可以在以前 Java 中没有的地方提供类型安全。所以过去如果你有一个充满字符串的列表,你必须这样做:

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

但现在你可以检索它而无需强制转换:

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

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

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

允许您使用以下方式实例化列表:

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

现在 ArrayList 上的每个函数都将返回一个 String,并且编译器知道这一点,因此不需要强制转换。这些函数中的每一个的定义都与上面的非常相似:

   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") 注释调用即可解决问题。但如果我理解正确的话,修复这个错误只会让你走上一条漫长的困惑之路,除非你理解我上面写的内容。

祝你好运!

Generics are there to provide type safety in places where you didn't previously have any in Java. So it used to be that if you had a list full of Strings you had to do:

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

but now you can retrieve it without casting it:

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

When you generify using the variable T, you are saying T is a placeholder for a specific type, which will be defined on the instance of the class at instantiation time. For instance:

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

allows you to instantiate the list with:

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

Now every function on ArrayList will return a String, and the compiler knows this so it doesn't require a cast. Each of those functions was defined much like yours above:

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

at compile time they become:

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

In your case, however, you seem to be trying to use T as a wildcard, which is different from a placeholder for a specific type. You might call this method three times for three different fields, each of which has a different return type, right? This means that, when you instantiate this class, you cannot pick a single type for T.

In general, look at your method signatures and ask yourself "will a single type be substituted for T for each instance of this class"?

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

If the answer is "no", that means this is not a good fit for Generics. Since each call will return a different type, you have to cast the results from the call. If you cast it inside this function, you're losing any benefits Generics would provide, and might as well save yourself the headaches.

If I've misunderstood your design, and T does refer to a single type, then simply annotating the call with @SuppressWarnings(value="unchecked") will do the trick. But if I've understood correctly, fixing this error will just lead you to a long road of confusion unless you grok what I've written above.

Good luck!

已下线请稍等 2024-09-02 12:52:56

正如上面所建议的,您可以指定字段的预期类型并调用强制转换方法。

还。您不需要传递参数对象的类。您可以通过调用 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();
    }
}

As suggested above, you can specify the expected type of the field and call the cast method.

Also. you don't need to pass argument object's class. You can find out what it is by calling obj.getClass()

This simplifies your code to

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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文