When calling findBySubstring()
from ArticleAutocompleteServiceImpl
- default method of interface is executed instead of implementation. Is it because of generics? What did I do wrong? How to keep this structure and run implemented method instead of default method? Please can someone help me with this?
EDIT:
If I remove default method from FindBySubstringRepositorySupport
and leave signature only, it works as expected. Downside is that I have to add dummy implementation method to all services that implements FindBySubstringRepositorySupport
and I use this method only on few that have real implementations.
ArticleAutocompleteServiceImpl.java
@Service
public class ArticleAutocompleteServiceImpl implements ArticleAutocompleteService {
private final ArticleRepository articleRepository;
public ArticleAutocompleteServiceImpl( final ArticleRepository articleRepository ) {
this.articleRepository = articleRepository;
}
@Override
public List<ArticleURINameDTO> autocomplete( final String q ) {
// calling repository method and expecting to run implemented method,
// not default mehtod of interface
final List<Article> lst =
this.articleRepository.findBySubstring( q, 10, ObjectStatus.ENABLED );
final Locale locale = ...
return lst.stream().map(a -> new ArticleURINameDTO(a, locale)).toList();
}
ArticleRepositoryImpl.java
public class ArticleRepositoryImpl implements ArticleExtendRepository {
@PersistenceContext
private EntityManager entityManager;
private final StoredProcService storedProcService;
public ArticleRepositoryImpl( final StoredProcService storedProcService ) {
this.storedProcService = storedProcService;
}
// implementation that should override default method of interface
@Override
public List<Article> findBySubstring(
final String namePart,
final Integer limit,
final ObjectStatus objectStatus
) {
return this.storedProcService.execute( ... );
}
}
ArticleRepository.java
public interface ArticleRepository extends
PagingAndSortingRepository<Article, Integer>,
CrudRepository<Article, Integer>,
ArticleExtendRepository
{
//
}
ArticleAutocompleteService.java
public interface ArticleAutocompleteService {
List<ArticleURINameDTO> autocomplete(String q);
}
FindBySubstringRepositorySupport.java
public interface FindBySubstringRepositorySupport<T> {
// default method to be run if no implementation is present
default List<T> findBySubstring(
final String namePart,
final Integer limit,
final ObjectStatus objectStatus
) {
throw new IllegalStateException( "Method not implemented" );
}
}
ArticleExtendRepository.java
public interface ArticleExtendRepository extends FindBySubstringRepositorySupport<Article> {
// some method signature not important here
long countByStatus( ObjectStatus objectStatus );
}
It was my bad design and lack of knowledge:
Rules for Default Method Conflict Resolution
When a class inherits a method with the same signature from more than one interface, we have the following three rules:
Classes will always win. If a class extends a parent class and implements one or more interfaces, the default method in the class or a superclass will take priority.
Otherwise the subinterfaces will take the next level of precedence. The default method in the most specific default providing interface will be the chosen one. (A subinterface is the one that extends an interface).
If the above rules are not applicable and a choice cannot be made, then the class that inherits from multiple interfaces has to override the method and provide an implementation. But it does not always have to provide the full implementation as there is a way for the class to choose one of the inherited default methods.
Source: https://javadevcentral.com/default-method-resolution-rules#:~:text=If%20a%20class%20extends%20a,will%20be%20the%20chosen%20one.