bertilmuth
bertilmuth

Reputation: 286

Implementing class of Java interface with generic types: warning "Type safety: The return type ... needs unchecked conversion to conform"

I have the following Java super interface:

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

and the following implementing (inner) class:

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

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

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

While the idOf() method and everything else works fine, the entityWithId() method causes the following compiler warning:

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>

Can anybody explain why this is the case exactly, and what would be a better alternative?

P.S. The TestingEntity is just a simple POJO with getters:

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

Upvotes: 1

Views: 181

Answers (2)

Andrew
Andrew

Reputation: 49606

<S extends T> S entityWithId(S entity, ID id);

The contract says "return something that extends T".

TestingEntity entityWithId(TestingEntity entity, UUID id)

The implementation says "return TestingEntity", or, in the interface's terms, "return T". It's a mismatch in definition, thus the warning.

If you were to follow the convention precisely, it would be like

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

@Sweeper makes a good point in the comments - you could have simply used T instead of <S extends T> (unless you have a good reason for it, which we can't see).

Upvotes: 1

Generous Badger
Generous Badger

Reputation: 421

The problem is the <S extends T> part of the method.

With this notation you define a new method type parameter S that is some specific subtype of T.

The way your entityWithId method is defined you could pass in a SubclassOfTestingEntity and also get a SubclassOfTestingEntity back.

If you just return the input in entityWithid that will work and callers will know that the return type of that method matches the type of the first parameter.

If, however, you somehow construct a new object in that method, then you'll have to do some shenanigans to ensure that the return type is actually of the same type as the input (or do an unsafe cast if you don't make sure).

In either way, you must re-defined the type-parameter in your implementation:

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

Also: there seems to be absolutely no reason for <S extends T> to be present on idOf: simply defining it as ID idOf(T entity) would be sufficient, work exactly the same and is much simpler to read.

Upvotes: 1

Related Questions