Java泛型何时需要而不是,切换是否有任何负面影响?

Java泛型何时需要而不是,切换是否有任何负面影响?,java,generics,junit,Java,Generics,Junit,给出了使用JUnit和Hamcrest匹配器的以下示例: Map<String, Class<? extends Serializable>> expected = null; Map<String, Class<java.util.Date>> result = null; assertThat(result, is(expected)); 编译器错误消息为: Error:Error:line (102)cannot find symbol

给出了使用JUnit和Hamcrest匹配器的以下示例:

Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));  
编译器错误消息为:

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>>>)
然后编译工作开始了

所以有三个问题:

为什么当前版本不能编译?虽然我模糊地理解了这里的协方差问题,但如果不得不解释的话,我当然无法解释。
首先,我必须告诉你,将资产评估方法改为Matcher有什么坏处吗?她做得很出色

基本思想是使用

<T extends SomeClass>
您是说expected可以包含表示实现可序列化的任何类的类对象。结果映射表示它只能保存日期类对象

当您传入结果时,您将T设置为字符串到日期类对象的精确映射,这与字符串到任何可序列化对象的映射不匹配

有一件事需要检查-你确定你想要上课而不是约会吗?字符串到类的映射听起来并不是非常有用,通常它只能将Date.Class作为值而不是Date的实例


至于泛化assertThat,其思想是该方法可以确保传入适合结果类型的匹配器。

您的原始代码没有编译的原因是我理解通配符的一种方法是认为通配符没有指定给定泛型引用可以拥有的可能对象的类型,但与此兼容的其他泛型引用的类型可能听起来令人困惑。。。因此,第一个答案在措词上具有很大的误导性

换句话说,List可以归结为:

Class<? extends Serializable> c1 = null;
Class<java.util.Date> d1 = null;
c1 = d1; // compiles
d1 = c1; // wont compile - would require cast to Date
您可以看到类引用c1可能包含一个长实例,因为给定时间的基础对象可能是List,但显然不能强制转换为Date,因为无法保证未知类是Date。它不是typsesafe,因此编译器不允许它

但是,如果我们引入一些其他对象,比如在您的示例中,List这个对象是Matcher,那么下面的内容将变为真:

List<Class<? extends Serializable>> l1 = null;
List<Class<java.util.Date>> l2 = null;
l1 = l2; // wont compile
l2 = l1; // wont compile
…但是,如果列表的类型变为?扩展T而不是T

List<? extends Class<? extends Serializable>> l1 = null;
List<? extends Class<java.util.Date>> l2 = null;
l1 = l2; // compiles
l2 = l1; // won't compile
我认为通过将Matcher更改为Matcher如果您使用

Map<String, ? extends Class<? extends Serializable>> expected = null;

感谢所有回答这个问题的人,这真的帮助我澄清了一些事情。最后,斯科特·斯坦奇菲尔德的答案与我最终理解它的方式最接近,但由于他第一次写这篇文章时我不理解他,所以我试图重申这个问题,希望其他人能从中受益

我将用列表的形式重申这个问题,因为它只有一个通用参数,这将使它更容易理解

示例中的参数化类(如List或Map)的目的是强制向下转换,并让编译器保证这是安全的,没有运行时异常

以列表为例。我的问题的实质是,为什么一个T型和一个列表的方法不会接受比继承链更进一步的列表。
List<java.util.Date> dateList = new ArrayList<java.util.Date>();
Serializable s = new String();
addGeneric(s, dateList);

....
private <T> void addGeneric(T element, List<T> list) {
    list.add(element);
}

在这种情况下,尽管junit方法实际上并不关心这些事情,但方法签名需要协方差,而协方差是它得不到的,因此它不会编译

关于第二个问题

Matcher<? extends T>
当T是一个对象时,会有真正接受任何东西的缺点,这不是API的意图。其目的是静态地确保匹配器与实际对象匹配,并且无法从该计算中排除对象

第三个问题的答案是不会丢失任何东西,就未经检查的功能而言,如果这个方法没有被泛化,JUnit API中就不会有不安全的类型转换,但是他们正在尝试完成其他事情——静态地确保两个参数可能匹配

在进一步思考和体验后编辑:


assertThat方法签名的一个大问题是试图将变量T等同于泛型参数T。这不起作用,因为它们不是协变的。例如,您可能有一个T,它是一个列表,但随后将编译器计算出的匹配传递给Matcher。如果它不是一个类型参数,事情就没问题了,因为List和ArrayList是协变的,但是因为泛型,就编译器而言,需要ArrayList,它不能容忍列表,因为我希望从上面可以清楚地看到原因。

我知道这是一个老问题,但我想分享一个我认为可以很好地解释有界通配符的示例。java.util.Collections提供了以下方法:

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}
如果我们有一个T的列表,这个列表当然可以包含扩展T的类型的实例。如果这个列表包含动物,这个列表可以同时包含狗和猫这两种动物。狗有一个属性woofVolume,猫有一个属性meowVolume。而我们可能希望根据这些属性进行排序
对于T的子类,我们怎么能期望这个方法做到这一点呢?比较器的一个限制是它只能比较一种类型T的两个东西。因此,只需要一个比较器就可以使这个方法可用。但是,这个方法的创建者认识到,如果某个东西是T,那么它也是T的超类的一个实例。因此,他允许我们使用T的比较器或T的任何超类,即?超级T.

在这种情况下,是的,我确实想要一个类的映射。我给出的示例被设计为使用标准JDK类,而不是我的自定义类,但在本例中,该类实际上是通过反射实例化的,并基于键使用。一个分布式应用程序,客户端没有可用的服务器类,只是用来做服务器端工作的类的键。我想我的大脑被卡住的地方是,为什么包含日期类型的类的映射不能很好地适应包含可序列化类型的类的映射类型。确保Serializable类型的类也可以是其他类,但它肯定包括Date类型。在确保为您执行强制转换的AssertServer上,matcher.matches方法并不重要,因此既然从未使用过t,为什么要涉及它?方法返回类型是voidAhhh-这就是我没有读取足够接近的资产的def所得到的结果。看起来这只是为了确保一个合适的匹配者通过…我不太明白-当你说不意味着。。。但是…:有什么区别?比如,一个已知但非特定的类符合前一个定义但不符合后一个定义的例子是什么?是的,这有点尴尬;不知道如何更好地表达它。。。说“?”是一个未知的类型,而不是一个匹配任何东西的类型是否更有意义?可能有助于解释这一点的是,对于任何扩展Serializable的类,您可以简单地使用它,这肯定会有所帮助,但可能听起来令人困惑的部分会被听起来令人困惑的部分所取代。接下来,根据这个解释,为什么使用matcheri的方法如果我们定义为List,它会做同样的事情呢?我说的是你的第二段。i、 多态性可以处理吗?是的,这正是我上面的答案的目的。不,这对情况没有帮助,至少在我尝试的方式下。我仍然不明白为什么我不能向上投射。为什么我不能将日期列表转换为可序列化的列表?@ThomasAhle,因为如果引用认为它是日期列表,那么当它们找到字符串或任何其他可序列化的对象时,就会遇到强制转换错误。我明白了,但是如果我以某种方式摆脱了旧引用,就像我从类型为list的方法返回列表一样,该怎么办?这应该是安全的,即使java不允许。这是非常有用的泛型、继承和子类型:这很奇怪,我使用的是java 8,既有公共静态void资产thattactual,Matcher Matcher,也有公共静态void资产thattresult,Matcher
Map<String, ? extends Class<? extends Serializable>> expected = null;
List<java.util.Date> dateList = new ArrayList<java.util.Date>();
Serializable s = new String();
addGeneric(s, dateList);

....
private <T> void addGeneric(T element, List<T> list) {
    list.add(element);
}
private <T> void genericAdd(T value, List<T> list)
T x = list.get(0);
list.add(value);
Matcher<? extends T>
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}