【问题标题】:Implementing class of Java interface with generic types: warning "Type safety: The return type ... needs unchecked conversion to conform"使用泛型类型实现 Java 接口的类:警告“类型安全:返回类型...需要未经检查的转换才能符合”
【发布时间】:2021-07-14 09:22:50
【问题描述】:

我有以下Java超级接口:

public interface IdProvider<T, ID> {
  <S extends T> ID idOf(S entity);
  ID nextId();
  <S extends T> S entityWithId(S entity, ID id);
}

以及以下实现(内部)类:

  class TestingEntityUuidProvider implements IdProvider<TestingEntity, UUID>{
    @Override
    public UUID idOf(TestingEntity entity) {...}

    @Override
    public UUID nextId() {...}

    @Override
    public TestingEntity entityWithId(TestingEntity entity, UUID id) {...}
  }

虽然idOf() 方法和其他一切工作正常,但entityWithId() 方法会导致以下编译器警告:

Type safety: The return type TestingEntity for entityWithId(TestingEntity, UUID) from the type TestingEntityUuidProvider needs unchecked conversion to conform to S from the type IdProvider<T,ID>

谁能解释一下为什么会出现这种情况,还有什么更好的选择?

附: TestingEntity 只是一个带有 getter 的简单 POJO:

public class TestingEntity {
  private final UUID id;
  private final String content;
...
}

【问题讨论】:

  • 在界面中,将类型参数S添加到idOfentityWithId背后的思路是什么?为什么不只是ID idOf(T entity);T entityWithId(T entity, ID id);?毕竟,这就是 TestingEntityUuidProvider 实现的。
  • 使用id提供者的代码使用了泛型子类,像这样:@Override public &lt;S extends T&gt; S save(S entity) { final ID idToBeSaved = idToBeSavedFor(entity); S entityToBeSaved = idProvider.entityWithId(entity, idToBeSaved); entities.put(idToBeSaved, entityToBeSaved); return entityToBeSaved; `}

标签: java generics interface


【解决方案1】:

问题出在方法的&lt;S extends T&gt; 部分。

使用这种表示法,您可以定义一个新的方法类型参数S,它是T某个特定子类型

entityWithId 方法的定义方式可以传入 SubclassOfTestingEntity 并返回 SubclassOfTestingEntity

如果您只返回 entityWithid 中的输入,这将起作用,并且调用者将知道该方法的返回类型与第一个参数的类型匹配。

但是,如果您以某种方式在该方法中构造了一个新对象,那么您将不得不做一些恶作剧以确保返回类型实际上与输入的类型相同(或者如果您 不要确定)。

无论哪种方式,您都必须在实现中重新定义类型参数:

@Override
public <S extends TestingEntity> S entityWithId(S entity, UUID id) {...}

另外:&lt;S extends T&gt; 似乎完全没有理由出现在 idOf 上:只需将其定义为 ID idOf(T entity) 就足够了,工作方式完全相同并且更易于阅读。

【讨论】:

  • ID idOf(T entity) 好点,我改了。现在关于另一个签名,我最初按照您的建议进行了操作,即创建了以下方法,但它导致编译错误:@Override public &lt;S extends TestingEntity&gt; S entityWithId(S entity, UUID id) { return new TestingEntity(id, entity.getContent()); }。为什么?该怎么办? (我猜这就是你所说的恶作剧)
  • 这不是该方法的有效实现。该方法承诺返回传入的任何类型 S 的对象。
【解决方案2】:
<S extends T> S entityWithId(S entity, ID id);

合同说“返回扩展 T 的东西”

TestingEntity entityWithId(TestingEntity entity, UUID id)

实现说“return TestingEntity”,或者,用接口的术语来说,“return T”。这是定义不匹配,因此发出警告。

如果你要严格遵守约定,那就是

<S extends TestingEntity> S entityWithId(S entity, UUID id)

@Sweeper 在 cmets 中提出了一个很好的观点 - 您可以简单地使用 T 而不是 &lt;S extends T&gt;(除非您有充分的理由,我们看不到)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-27
    • 2015-06-20
    • 1970-01-01
    • 2015-12-10
    • 1970-01-01
    • 2022-11-28
    相关资源
    最近更新 更多