javac编译器的差异性问题

4

更新:这似乎与Eclipse有关,而不是与Hudson有关,因此我已相应地更新了问题。

在命令行上运行Maven时,我遇到了一些编译器错误,但我们小组中的所有开发人员在Eclipse中都可以正常工作(一些通用细节,请参见下文)。为什么会有差异,并该如何处理?

代码失败的样子如下:

299 private <T extends ProductClassDTO> List<T> convertProductClass(List<? extends ProductClassDTO> fromList) {
300     List<T> toList = new ArrayList<T>();
301     for (ProductClassDTO from : fromList) {
302         T to = convert(from);
303         toList.add(to);
304     }
305     return toList;
306 }

以下是构建服务器上的错误:

[ERROR] ...java:[302,26] type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,com.volvo.protom.util.dto.ProductClassDTO

我知道在Stack Overflow上有其他关于此问题的问答,但它们似乎不适用于这个特定的问题,因为更改为T to = <T>convert(from)并不能解决问题,也许我应该尝试其他方法?我猜测错误是指这个类中有多个convert方法,而且有不止一个与之匹配。
谢谢!
更新 2:这些是转换签名:
private void convert(TestObjectDTO from, TestObjectDTO to);
private <T extends TestObjectDTO> T convert(TestObjectDTO from);
private void convert(ProductClassDTO from, ProductClassDTO to);
private <T extends ProductClassDTO> T convert(ProductClassDTO from);
private void convert(TestObjectTypeDTO from, TestObjectTypeDTO to);
private <T extends TestObjectTypeDTO> T convert(TestObjectTypeDTO from);

可能与此相关 https://dev59.com/hnRC5IYBdhLWcg3wYP-h - stacker
这是事实,但正如我所提到的,在我们开发人员在Windows下使用的编译器中,将<T>添加到第302行并不起作用。 - Jonas Byström
1
我希望 this.<T>convert(from); 能够正常工作。 - McDowell
1
我不能告诉你该怎么做,但我可以告诉你为什么会不同 - Eclipse不使用JDK的Java编译器,它有自己的编译器,因此可能存在差异,尽管这些情况现在非常罕见。我个人过去几次观察到代码在Eclipse中编译而在JDK或反之亦然时无法编译。这就是为什么将构建放在CI服务器上,而不是IDE之外,始终非常重要的原因。 - maximdim
1
我发现Eclipse是正确的,而javac有一些错误,但我们能够绕过它们。 - SteveD
显示剩余3条评论
2个回答

0
Eclipse和JDK javac略有不同,请参见@maximdim的评论。始终从命令行运行以确保兼容性(尽管Eclipse的javac似乎更正确)。

-1

我猜这些方法的签名不是他们应该有的。 泛型方法中,泛型参数仅出现在返回类型中(例如<T extends ProductClassDTO> T convert(ProductClassDTO))通常不是您想要的。

这意味着调用者可以合法地调用该方法并将结果强制转换为ProductClassDTO的任何可能子类型。 通常只有一个可能的值符合此要求:null。 因此,convert方法总是必须返回null才能保证类型安全。否则,在调用代码中可能会发生ClassCastException。

请记住,由于类型擦除,convert方法无法知道调用方希望T成为哪种类型,因此它们不能根据T的值返回不同类型的实例。

同样的问题也出现在convertProductClass方法中,虽然这里情况没有那么糟糕。该方法需要返回一个列表,其中只包含调用者可以解释为ProductClassDTO的任何子类型的值。唯一满足此条件的值是null、空列表和仅包含null值的列表。所有其他情况可能会导致在使用列表时某处发生ClassCastException

你可以做以下几点:

  • 如果转换方法始终返回相同的类型,请将方法的返回类型更改为此类型。
  • 如果调用者不关心具体类型,但只需要知道它是ProductClassDTO的子类型,则将返回类型分别更改为ProductClassDTOList<ProductClassDTO>
  • 如果convert方法应根据调用者的要求返回类型的实例,则需要通过向所有这些方法传递Class<T>的实例来显式告诉它们要返回的类型。然后,convert方法可以使用此对象创建适当类型的新实例。

如果我将其分配给ProductClassDTO(它是一个接口)的实现类,它就可以工作。传递Class<T>是我想要避免的,因此没有这样的东西。如果我想要一些其他类型(通过List<?>),则需要对List<ProductClassDTO>进行转换,这变得很冗长。无论如何,将其转换为T解决了问题,因此我将坚持使用它,而不是添加大量不必要的代码。 - Jonas Byström
@Jonas 现在它可以工作了,因为调用者和转换方法恰好使用相同的 T 值。但是你的编译器会发出警告,因为当你更改其中一个时,它可能会在某一天出现问题(那时不会有任何编译器警告或错误,只会在运行时抛出异常)。因此,我建议现在添加代码,以免日后进行调试。 - Philipp Wendler
此外,如果调用方和转换方法都同意,为什么不直接使用 ProductClassDTO 作为返回类型呢?或者它的实现类型?从您的解释中我看不出有任何理由不这样做。 - Philipp Wendler
这将需要在十几个地方进行“外部”转换,因此保留单个运行时异常比保留十二个更好,这是我的理由。 ;) - Jonas Byström

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接