【问题标题】:Java Self bound genericJava 自绑定泛型
【发布时间】:2017-01-26 07:13:39
【问题描述】:

我有像这样的自界泛型:

public interface SelfConfigurable<E extends SelfConfigurable<E>> {
    E configure(JsonObject settings);
}

还有另一个接口,也是泛型类型,扩展了我之前的接口:

public interface ConfigurableSegmentFilter<T> extends SegmentFilter<T>, SelfConfigurable<ConfigurableSegmentFilter<T>> {
}

我也有这样的实现

public abstract class ConfigurableSegmentFilterSkeleton<T> implements ConfigurableSegmentFilter<T> {
    @Override
    public ConfigurableSegmentFilter<T> configure(JsonObject settings) {
     ... }
}

我正在通过反射实例化对象,并希望在添加到列表之前对其进行配置:

List<ConfigurableSegmentFilter<?>> result = ...

ConfigurableSegmentFilter newFilter = Reflection.newInstance() + casting

result.add(newFilter.configure(...)); <-- compile error 'cannot be applien to SelfConfigurable' but why?

//when i call to configure directly i am getting:
SelfConfigurable configure = newFilter.configure(...) <-- why SelfConfigurable??

我得到一个编译错误!它告诉我 xxx.configure() 返回的是 SelfConfigurable 接口而不是 ConfigurableSegmentFilter,我不明白为什么会这样。

还有一件事,当我用通配符绑定 newFilter 时,事情开始按预期工作

List<ConfigurableSegmentFilter<?>> result = ...

ConfigurableSegmentFilter<?> newFilter = ... //<-- bound with <?>

result.add(newFilter.configure(...)); <-- No error! But why?

ConfigurableSegmentFilter vs ConfigurableSegmentFilter&lt;?&gt;的唯一区别

【问题讨论】:

  • 发生编译器错误是因为您使用的是原始类型。请参阅标题'原始类型是该类型的擦除'下的stackoverflow.com/a/2770692/2891664
  • 什么是JinniSegmentFilter????
  • 对不起,我错误地添加了它。我知道我使用的是原始类型,无论如何我无法理解它背后的逻辑。 ConfigurableSegmentFilter> 应该和 ConfigurableSegmentFilter 一样,等于 ConfigurableSegmentFilter,完全没有界限。
  • @Gelerion 不,ConfigurableSegmentFilter> 与 ConfigurableSegmentFilter 不一样?可以代表任何东西(例如字符串)。在泛型中,即使 B 扩展了 A,Clazz 也不能从 Clazz 分配。这可能看起来不直观,但有一些原因可以避免运行时强制转换异常。
  • @Gelerion 我不得不说我很惊讶您在 result.add(...) 中没有收到错误。我认为编译器无法分辨出这两个“?”正在引用相同的类型。这真的是手头问题的所有相关代码还是您省略了一些?

标签: java generics


【解决方案1】:

“原始”类型与类型参数设置为“对象”的泛型相同是不正确的。

这样做的原因是为了避免运行时强制转换异常。

例如,让我们考虑一个涉及“java.util.List”类的更简单的场景:

List a = ... # a elements could be a mix of any type.
List<?> b = ... # b elements are all instance of a known class '?'.
List<Object> c = ... # c elements are all instances of Object.
List<String> d = ... # d elements are all instances of String.

应该清楚为什么将元素从任何其他列表移动到“d”的操作肯定会出现问题,因为不能保证这些元素是“字符串”

但是其他组合呢?

  1. a、c 或 d -> b.

    b.addAll(c); // error!!!
    

    '?'这里表示一个未知类型...例如它可能是字符串,在这种情况下,我们回到明确的情况 c -> d。设置此限制的原因是为了防止运行时异常,当其他一些实际上知道集合的类型参数(例如 String)并且正在使用它的元素的代码拉取一个非字符串实例导致运行时强制转换异常时.

  2. b, a, d -> c 或 b, c, d -> a.

    允许,毕竟“?”所有实例都是“对象”,所以那里没有问题。一个

还请注意,一些参考分配我是一个问题(至少是一个警告):

   c = a; // unchecked warning; 
   c = d; // error as now you would be able to add non-strings to 'd' thru 'c'.
   c = b; // error, same as above where the the element type is unknown.
   a = d; a = b; // same as with 'c'.
   d = c; d = a; // error for obvious reason. 

【讨论】:

  • 感谢您的回答和解释。我确实理解“原始”类型与通配符泛型不同。我阅读了 Java Generics and Collections 书,无论如何我无法理解我的具体情况有什么不同。我没有省略任何相关代码,只是在一个新的干净项目上尝试过,当我使用原始对象而不是通配符有界对象时,newFilter.configure(..) 返回我 SelfConfigurable。也许是因为桥接方法?
【解决方案2】:

当您使用原始类型的表达式时,您“关闭”了您使用该表达式执行的操作中的所有泛型,包括方法签名和超类型签名。这意味着,原始类型ConfigurableSegmentFilter 仅扩展了原始类型SelfConfigurable,其实例方法.configure() 因此返回E 的擦除,即SelfConfigurable

这就是为什么你应该避免使用原始类型,因为它们只是为了向后兼容。当类型参数未知时,可以使用通配符参数化类型。

【讨论】:

    猜你喜欢
    • 2020-10-31
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 2017-08-23
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多