javagenericspecs

PECS on the result type within an interface?


Let me say I have an interface with a generic parameter.

@NoRepositoryBean
interface ISomeRepository<ENTITY extends AbstractSome<ENTITY>> {

    Streamable<? extends ENTITY> findAllByOther(String other, Pageable pageable);

    Streamable<ENTITY> findAllByAnother(String another, Pageable pageable);

    // <? extends ENTITY> vs. <ENTITY>
}

The Streamable is just an interface, and actual repository interface should override the type such as Page or Slice.

class Some extends AbstractSome<Some> {
}
@NoRepositoryBean
interface SomeRepository extends ISomeRepository<Some> {

    Page<Some> findAllByOther(String other, Pageable pageable);

    Slice<Some> findAllByAnother(String another, Pageable pageable);
}

Both overridden methods work as expected.

Does the <? extends ENTITY> have any meaning more than the <ENTITY> part?


Solution

  • It does have more meaning than just ENTITY. The implementation of ISomeRepository<Some> can return Streamables of subclasses of Some. Suppose there is a Some subclass called SomeChild, you can write

    public class SomeRepository extends ISomeRepository<Some> {
        public Page<SomeChild> findAllByOther(String other, Pageable pageable) {
            // ...
        }
    
        // ...
    }
    

    Though, in the context of a "repository", you are probably not going to write something like that anyway.

    If in your implementation of findAllByOther, you call some other method that returns a Page<? extends Some>, then having Streamable<? extends ENTITY> as the return type allows you to return that directly. On the other hand, you cannot return a Page<? extends Some> from a method that is declared to return Streamable<Some>.

    ? extends ENTITY also affects the caller. If the caller calls findAllByOther through the ISomeRepository<Some> interface, they cannot call methods of Streamable that take T as an input. For example,

    Streamable<? extends Some> streamable = someRepo.findAllByOther(...);
    Some somethingElse = ...;
    streamable.and(somethingElse); // this does not compile