public interface EntityId {
...
EntityId cloneWithNewId(long id);
}
public interface Ticket extends EntityId {
/// cloneWithNewId - is not mentioned in this interface
}
public record TicketImpl(...)
@Override
public Ticket cloneWithNewId(long id) {...}
The compiler gives the error, when I write in my unit test the line with "cloneWithNewId" call:
@Test
void shouldBookTicket() {
Ticket ticket = new TicketImpl(7L, 8L, Ticket.Category.PREMIUM, 21);
Ticket expectedTicket = ticket.cloneWithNewId(1L); // compiler error in this line
//...
}
"EntityId cannot be converted to Ticket. Required type: Ticket. Provided: EntityId"
Any ideas why? Seems to be not much different from classic examples for covariant return type.
It works if I make EnityId interface generic
public interface EntityId<? extends T> {
...
T cloneWithNewId(long id);
}
public interface Ticket extends EntityId<Ticket> {
/// cloneWithNewId - is not mentioned in this interface
}
It also works if I add the method declaration to Ticket interface:
public interface Ticket extends EntityId {
Ticket cloneWithNewId(long id);
}
But I do not understand, why it does not work when I override method from extended interface.
In the unit test, you are not invoking the method with the narrowed return type.
The compiler raises an error here, because the return type of
Ticket#cloneWithNewId(long)isEntityId, as it is inherited fromEntityId. Note that, when confronted with a compiler error, it does not make sense to argue about a specificTicketImplinstance, as you are not in a runtime. Instead only the declared type of each local variable is relevant.This is also why
will fix the compiler error.
Another possible fix is to change the unit test to
as that will "point" the compiler to the method declaration in
TicketImpl, whose return type is narrowed toTicket.