Java 泛型何时需要 <?扩展 T>而不是 <T>切换有什么缺点吗? [英] When do Java generics require <? extends T> instead of <T> and is there any downside of switching?

查看:29
本文介绍了Java 泛型何时需要 <?扩展 T>而不是 <T>切换有什么缺点吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下示例(使用带有 Hamcrest 匹配器的 JUnit):

Given the following example (using JUnit with Hamcrest matchers):

Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));  

这不会与以下 JUnit assertThat 方法签名一起编译:

This does not compile with the JUnit assertThat method signature of:

public static <T> void assertThat(T actual, Matcher<T> matcher)

编译器错误信息是:

Error:Error:line (102)cannot find symbol method
assertThat(java.util.Map<java.lang.String,java.lang.Class<java.util.Date>>,
org.hamcrest.Matcher<java.util.Map<java.lang.String,java.lang.Class
    <? extends java.io.Serializable>>>)

但是,如果我将 assertThat 方法签名更改为:

However, if I change the assertThat method signature to:

public static <T> void assertThat(T result, Matcher<? extends T> matcher)

然后编译工作.

所以三个问题:

  1. 为什么当前版本无法编译?虽然我对这里的协方差问题有模糊的理解,但如果必须的话,我当然无法解释.
  2. assertThat 方法更改为 Matcher< 有什么缺点吗?扩展 T>?如果您这样做,是否还有其他情况会中断?
  3. 在 JUnit 中对 assertThat 方法进行泛化有什么意义吗?Matcher 类似乎不需要它,因为 JUnit 调用了matches 方法,该方法没有使用任何泛型类型化,并且看起来像是试图强制类型安全,但它不做任何事情,因为 Matcher 实际上不会匹配,无论如何测试都会失败.不涉及不安全的操作(或看起来如此).
  1. Why exactly doesn't the current version compile? Although I vaguely understand the covariance issues here, I certainly couldn't explain it if I had to.
  2. Is there any downside in changing the assertThat method to Matcher<? extends T>? Are there other cases that would break if you did that?
  3. Is there any point to the genericizing of the assertThat method in JUnit? The Matcher class doesn't seem to require it, since JUnit calls the matches method, which is not typed with any generic, and just looks like an attempt to force a type safety which doesn't do anything, as the Matcher will just not in fact match, and the test will fail regardless. No unsafe operations involved (or so it seems).

作为参考,这里是assertThat的JUnit实现:

For reference, here is the JUnit implementation of assertThat:

public static <T> void assertThat(T actual, Matcher<T> matcher) {
    assertThat("", actual, matcher);
}

public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
    if (!matcher.matches(actual)) {
        Description description = new StringDescription();
        description.appendText(reason);
        description.appendText("
Expected: ");
        matcher.describeTo(description);
        description
            .appendText("
     got: ")
            .appendValue(actual)
            .appendText("
");

        throw new java.lang.AssertionError(description.toString());
    }
}

推荐答案

首先 - 我必须引导您到 http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html -- 她做得非常出色.

First - I have to direct you to http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html -- she does an amazing job.

基本思路就是你用

<T extends SomeClass>

当实际参数可以是 SomeClass 或它的任何子类型时.

when the actual parameter can be SomeClass or any subtype of it.

在你的例子中,

Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));

您是说 expected 可以包含代表任何实现 Serializable 的类的 Class 对象.您的结果地图说它只能保存 Date 类对象.

You're saying that expected can contain Class objects that represent any class that implements Serializable. Your result map says it can only hold Date class objects.

当你传入结果时,你将 T 设置为 StringMapDate 类对象,它不将 StringMap 与任何 Serializable 匹配.

When you pass in result, you're setting T to exactly Map of String to Date class objects, which doesn't match Map of String to anything that's Serializable.

要检查一件事——您确定要Class 而不是Date?StringClass 的映射一般来说听起来不是很有用(它所能保存的只是 Date.class 作为值而不是比 Date)

One thing to check -- are you sure you want Class<Date> and not Date? A map of String to Class<Date> doesn't sound terribly useful in general (all it can hold is Date.class as values rather than instances of Date)

至于泛化assertThat,思路是方法可以保证传入一个符合结果类型的Matcher.

As for genericizing assertThat, the idea is that the method can ensure that a Matcher that fits the result type is passed in.

这篇关于Java 泛型何时需要 &lt;?扩展 T>而不是 &lt;T&gt;切换有什么缺点吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆