Multilevel inheritance, generic, raw type - java

I need a solution, is my code structure correct, or is it a bad code structure?
The main problem is that compiler swears at the raw type.
Is this code structure bad? Why? Is it correct?
#NoRepositoryBean
public interface EntityRepository<E, ID extends Serializable> extends JpaRepository<E, ID> {
}
#NoRepositoryBean
public interface CatalogEntityRepository<C extends Catalog.Main, E extends CatalogEntity, ID extends Serializable>
extends EntityRepository<E, ID>, Catalog.Able<C> {
}
#Repository
public interface OrganizationEntityRepository extends CatalogEntityRepository<OrganizationCatalog, OrganizationEntity, Long> {
#Override
default OrganizationCatalog getCatalog() {
return Context.getApplicationContext().getBean(OrganizationCatalog.class);
}
}
Is this code structure bad? Why? Is it correct?
// Here I have some context class (CatalogEntityRepositoryContextImpl) that can find the repository which I need.
public interface EntityRepositoryContext<R extends EntityRepository> {
List<R> getAll();
Optional<R> find(String parentAlias);
default R get(String parentAlias) {
return find(parentAlias).orElseThrow(() -> {
throw new NoSuchEntityRepositoryException(parentAlias);
});
}
}
public interface CatalogEntityRepositoryContext<R extends CatalogEntityRepository>
extends EntityRepositoryContext<R> {
#Override
default Optional<R> find(String catalogAlias) {
return getAll().stream()
.filter(it -> Objects.equals(it.getCatalog().getAlias(), catalogAlias))
.findFirst();
}
#Override
default R get(String catalogAlias) {
return find(catalogAlias).orElseThrow(() -> {
throw new NoSuchCatalogEntityRepositoryException(catalogAlias);
});
}
}
#Component
#RequiredArgsConstructor
public class CatalogEntityRepositoryContextImpl
implements CatalogEntityRepositoryContext<CatalogEntityRepository> {
#Getter
private final List<CatalogEntityRepository> all;
}
And my main problem with raw type is: the code works, but I need confirmation from the guru that this code is not too bad, or give me some useful solutions, please.
I know raw type is bad practice. But I can't figure out how I can fix this in my situation.
public interface CatalogEntityService<C extends Catalog.Main, DS extends CatalogEntityDto.Single, DL extends CatalogEntityDto.Listable>
extends EntityService<DS, DL>, Catalog.Able<C> {
private CatalogEntityRepository getRepository() {
CatalogEntityRepositoryContextImpl repositoryContext = Context.getCatalogEntityRepositoryContext();
return repositoryContext.get(getCatalog().getAlias());
}
#Transactional
#Override
default Long create(DS dto) {
var repository = getRepository();
var mapper = getMapper();
var entity = mapper.mapToEntity(dto);
var savedEntity = repository.save(entity); // HERE IS THE RAW TYPE! "Unchecked call to 'save(S)' as a member of raw type 'app.unibas.core.catalog.repository.CatalogEntityRepository'"
return (Long) savedEntity.getId();
}
}

Related

How to store and retrieve generic types

I have a base repository interface and several repository interfaces extending it
interface VehicleRepository<T extends Vehicle> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> {
}
interface CarRepository extends VehicleRepository<Car> {
}
interface BikeRepository extends VehicleRepository<Bike> {
}
I also have several JPA specifications
class Specification<T extends Vehicle> implements Specification<T>{
}
class CarSpecification extends Specification<Car> {
}
class BikeSpecification extends Specification<Bike> {
}
Now I have a method that takes in different sub-types of Vehicle and I would like them to call their respective Repository and Specification. How should I do it?
I have tried wrapping them in List<VehicleRepository<**SOMETHING**>> repoList and List<Specification<**SOMETHING**>> specList, and would like to call them based on inputs
public class MyController {
// #Autowire
List<VehicleRepository<SOMETHING>> repoList;
List<Specification<SOMETHING>> specList;
#GetMapping("...")
public ResponseEntity<List<? extends Vehicle>> getRawEventByCriteria(int vehicleType) {
// What I am doing...
if(vehicleType == 0) {
List<Car> carList = ((VehicleRepository<Car>)repoList.get(0))
.findAll((Specification<Car>)specList.get(0));
} else if (vehicleType == 1) {
List<Bike> bikeList = ((VehicleRepository<Bike>)repoList.get(1))
.findAll((Specification<Bike>)specList.get(1));
} else if (...) {
}
// What I want to do...
List<vehicle.class> carList = ((AUTO SOMETHING)repoList.get(vehicleType))
.findAll((AUTO SOMETHING)specList.get(vehicleType));
return new ResponseEntity<>(retval, HttpStatus.OK);
}
}
For reference, findAll() is defined as follow
interface JpaSpecificationExecutor<T> {
List<T> findAll(#Nullable Specification<T> spec);
}
Currently I am leaving SOMETHING as a rawtype, and it works without me having to cast everything, but I feel that it is not a good approach.

How get Instance using Generic approach

I am trying to build one page assembler using generic approach. Following is piece of code.
In this code IEntity is marker interface for DB entities.
public abstract class PageHrefBuilder implements HrefBuilder<IEntity, PageLinks> {
#Override
public PageLinks buildLinks(IEntity entity) {
return null;
}
}
public interface HrefBuilder<E extends IEntity, L extends Links> {
public L buildLinks(E dto);
}
So we have one interface says can build links using IEntity type of class and return Links type of value. So I want to write some common code in abstract class and abstract class does not know what type entity it suppose to deal with.
For example entity can UserEntity, OrderEntity and so on.
So my question is how in abstract class I can get the class instance to build links with using instanceof or if else approach.
Could someone help me on this.
You can make your buildLinks method to take Class<T> parameter instead of the object you have to pass.
so it will be
public L buildLinks(Class<E> dto);
then in your abstract class
#Override
public PageLinks buildLinks(Class<IEntity> dto) {
return dto.newInstance();
}
Hope this answer could help you.
This got a bit over-engineered but it works and all generics are proper. Hopefully it shows you how you can build your solution:
public class GenericsSample {
public abstract class Links {
String data;
public Links(String data) {
this.data = data;
}
}
public class UserLinks extends Links {
public UserLinks(String data) {
super(data);
}
}
public class PageLinks extends Links {
public PageLinks(String data) {
super(data);
}
}
public abstract class IEntity<L extends Links> {
public abstract L buildLinks();
}
public class UserEntity extends IEntity<UserLinks> {
#Override
public UserLinks buildLinks() {
return new UserLinks("From UserEntity");
}
}
public class PageEntity extends IEntity<PageLinks> {
#Override
public PageLinks buildLinks() {
return new PageLinks("From PageEntity");
}
}
public interface HrefBuilderInterface<E extends IEntity<L>, L extends Links> {
public L buildLinks(E dto);
}
public class HrefBuilder<E extends IEntity<L>, L extends Links> implements HrefBuilderInterface<E, L> {
#Override
public L buildLinks(E entity) {
return entity.buildLinks();
}
}
public static void main(String[] args) {
new GenericsSample().go();
}
private void go() {
System.out.println(new HrefBuilder<UserEntity, UserLinks>().buildLinks(new UserEntity()).data);
System.out.println(new HrefBuilder<PageEntity, PageLinks>().buildLinks(new PageEntity()).data);
}
}
Also note that thanks to this setup the following is not valid:
System.out.println(new HrefBuilder<PageEntity, PageLinks>().buildLinks(new UserEntity()).data);
You can declare abstract method in abstract class to get new instance of Links interface:
abstract class PageHrefBuilder<E extends IEntity, L extends Links> implements HrefBuilder<E, L> {
#Override
public L buildLinks(E dto) {
L links = newLinks();
// ...
return links;
}
protected abstract L newLinks();
}
and implement getting new instance in realization.

How to handle generic interfaces implementation and avoid #SuppressWarnings?

I am trying to make a generic handling of different service implementations and I constantly receive the "Unchecked call due to raw type" error.
I have tried several implementations, but could not quite understand what is the issue here.
I have the following model:
public abstract class Fruit {
public List<String> vitamins;
public String originCountry;
// getters and setters omitted
}
The concrete implementation are the following:
public class Kiwi extends Fruit {
}
public class Pineapple extends Fruit {
}
I have the following interface:
public interface FruitCheckService<T extends Fruit> {
List<String> compareVitaminsFromDifferentCountries(T firstFruit, T secondFruit);
Class<T> getImplementation();
}
With implementations for the 2 models given above:
#Service
public class KiwiCheckServiceImpl implements FruitCheckService<Kiwi> {
#Override
public List<String> compareVitaminsFromDifferentCountries(Kiwi firstFruit, Kiwi secondFruit) {
// some implementation
return new ArrayList<>();
}
#Override
public Class<Kiwi> getImplementation() {
return Kiwi.class;
}
}
And pineapple:
#Service
public class PineappleCheckServiceImpl implements FruitCheckService<Pineapple> {
#Override
public List<String> compareVitaminsFromDifferentCountries(Pineapple firstFruit, Pineapple secondFruit) {
// some implementation
return new ArrayList<>();
}
#Override
public Class<Pineapple> getImplementation() {
return Pineapple.class;
}
}
I have the following class which is manupulating with the different beans:
#Service
public class FruitServices {
private Map<Class, FruitCheckService> beansMap;
#Autowired
public FruitServices(List<FruitCheckService> fruitCheckServices) {
beansMap = new HashMap<>();
fruitCheckServices
.forEach(
fruitCheckService -> {
Class implementation = fruitCheckService.getImplementation();
beansMap.put(implementation, fruitCheckService);
}
);
}
public FruitCheckService getFruitCheckService(Class clazz) {
return beansMap.get(clazz);
}
}
At the end, this is the service where I am calling this:
#Component
public class BusinessService {
#Autowired
private FruitServices fruitServices;
public void compareVitamins(Fruit one, Fruit two) {
Class<? extends Fruit> aClass = one.getClass();
FruitCheckService fruitCheckService = fruitServices.getFruitCheckService(aClass);
List<String> result = fruitCheckService.compareVitaminsFromDifferentCountries(one, two);
}
}
How to use correctly the fruitCheckService without receiving "Unchecked call to 'compareVitaminsFromDifferentCountries(T, T)' as a member of raw type 'exercise2.service.FruitCheckService'"?
The problem is that you are using the raw type Class in your service definition. To get around this problem, you can use the bounded wildcard type ?, as such:
class FruitServices {
private Map<Class<? extends Fruit>, FruitCheckService<? extends Fruit>> beansMap;
public FruitServices(List<FruitCheckService<? extends Fruit>> fruitCheckServices) {
beansMap = new HashMap<>();
fruitCheckServices
.forEach(
fruitCheckService -> {
Class<? extends Fruit> implementation = fruitCheckService.getImplementation();
beansMap.put(implementation, fruitCheckService);
}
);
}
public FruitCheckService<? extends Fruit> getFruitCheckService(Class<? extends Fruit> clazz) {
return beansMap.get(clazz);
}
}
For class BusinessService things are going to be trickier. First, it is necessary to make compareVitamins a generic method to ensure both fruits are of the same type. But even then, I don't believe you can avoid an unchecked cast from the result of getFruitCheckService because, as FruitServices has to deal with services for different types of fruit, you can't have a precise type parameter for the return value of getFruitCheckService.
class BusinessService {
private FruitServices fruitServices;
public <T extends Fruit> void compareVitamins(T one, T two) {
#SuppressWarnings("unchecked")
FruitCheckService<T> fruitCheckService =
(FruitCheckService<T>) fruitServices.getFruitCheckService(one.getClass());
List<String> result = fruitCheckService.compareVitaminsFromDifferentCountries(one, two);
}
}

Unable to MockUp a generic interface in JMockit

I want to mock a generic interface:
public interface IModel<T, S> {
public S classify(T entity);
}
This interface is sub-classed by 3 concrete classes: TextModel, ImageModel, ScoringModel. Each of these concrete classes have different T and S parameters.
I wrote a generic method that receives the concrete model class as an argument and generates a mocked version of the model:
private <T extends IModel<?, ?>> T mockModel(Class<T> modelClass) {
return new MockUp<T>() {
#Mock public Object classify(Object entity) { return null; }
}.getMockInstance();
}
I know that IModel::classify has generic types for both its input and output, but I haven't found a way to use the actual generic method within the mockup.
When calling this method I get an IllegalArgumentException:
java.lang.IllegalArgumentException: Value of type com.classificationmanager.model.$Impl_IModel incompatible with return type com.classificationmanager.model.TextModel of com.classificationmanager.model.TextModelFactory#createModel(com.classificationmanager.model.ModelDescriptor)
at com.classificationmanager.model.ModelFetcherTest$5.(ModelFetcherTest.java:110)
at com.classificationmanager.model.ModelFetcherTest.mockAllFactories(ModelFetcherTest.java:109) ....... (spared you the rest)
I thought that getting and returning an Object instead of T and S was the problem, but I get the same exception when removing the mocked method and just mocking the class:
private <T extends IModel<?, ?>> T mockModel(Class<T> modelClass) {
return new MockUp<T>() {
}.getMockInstance();
}
I could do a switch-case and return a concrete class but that would just be nasty.
Any workaround involving the Expectations API would also work for me.
10x
Maybe the following examples can help (although I still don't understand the question - probable case of the XY problem).
public final class ExampleTest {
public interface IModel<T, S> { S classify(T entity); }
static class TextModel implements IModel<Integer, String> {
#Override public String classify(Integer entity) { return "test"; }
}
static class ImageModel implements IModel<String, Image> {
#Override public Image classify(String entity) { return null; }
}
#Test
public void createNonMockedInstanceForAnyModelClass() {
IModel<Integer, String> m1 = mockModel(TextModel.class);
String s = m1.classify(123);
IModel<String, Image> m2 = mockModel(ImageModel.class);
Image img = m2.classify("test");
assertEquals("test", s);
assertNull(img);
}
<T extends IModel<?, ?>> T mockModel(Class<T> modelClass) {
// Or use newUninitializedInstance in case the model class doesn't
// have a no-args constructor.
return Deencapsulation.newInstance(modelClass);
}
#Test
public void mockAllModelImplementationClassesAndInstances(
#Capturing IModel<?, ?> anyModel
) {
IModel<Integer, String> m = new TextModel();
String s = m.classify(123);
assertNull(s);
}
}

Java Generics and Enum, loss of template parameters

I have a fairly complicated structure, and it is not working as intended. This is what I did:
public interface ResultServiceHolder {
<M, ID extends Serializable, BO extends BusinessObject<M, ID>> ResultService<M, ID, BO> getService();
}
public enum ResultTypes implements ResultServiceHolder {
RESULT_TYPE_ONE {
#Override
public ResultOneService getService() { //unchecked conversion?
return serviceInitializer.getResultOneService();
}
},
RESULT_TYPE_TWO {
#Override
public ResultTwoService getService() { //unchecked conversion?
return serviceInitializer.getResultTwoService();
}
},
RESULT_TYPE_THREE {
#Override
public ResultThreeService getService() { //unchecked conversion?
return serviceInitializer.getResultThreeService();
}
};
protected ServiceInitializer serviceInitializer;
protected void setServiceInitializer(ServiceInitializer serviceInitializer) {
this.serviceInitializer = serviceInitializer;
}
#Component
public static class ServiceInitializer {
#Autowired
private ResultOneService resultOneService;
#Autowired
private ResultTwoService resultTwoService;
#Autowired
private ResultThreeService resultThreeService;
#PostConstruct
public void init() {
for(ResultTypes resultType : ResultTypes.values()) {
resultType.setServiceInitializer(this);
}
}
//getters
}
}
The purpose was to generalize the call based on enums, and rather, just be able to iterate on the array of enums.
for(ResultServiceHolder resultServiceHolder : ResultTypes.values()) {
if(resultServiceHolder.equals(post.getPostResultTypeCode())) {
return resultServiceHolder.getService().createResultSearchCriteriaResponse(postId);
}
}
And this is working fine and dandy. However, if I'd say
ResultTypes.RESULT_TYPE_ONE.getService().getRepository()
Then it is a BaseRepository<Object, Serializable> rather than a BaseRepository<ResultTypeOne, Long>. The method resultTypeHolder.getService() gives back ResultService<M, ID, BO>, but in the end, it becomes Object andSerializable.
What am I doing wrong? How can I retain the generic parameter types?
I'd like to add that yes, I do realize the problem is somewhere with the unchecked casting. But the services are defined as
public interface ResultTypeOneService
extends ResultService<ResultTypeOne, Long, ResultTypeOneBO> {
}
And I don't know why the types are not inferred.
EDIT: Technically, it works if I explicitly infer them:
ResultTypes.RESULT_TYPE_ONE.<ResultTypeOne, Long, ResultTypeOneBO>getService().getRepository()
But it ought to be automatic, why is it not working automatically? Am I supposed to provide it with some kind of object that contains the type? Why is the return type not enough for that?
EDIT2: The superclass of the ResultTypeOne is
#SuppressWarnings("serial")
#EntityListeners(EntityListener.class)
#MappedSuperclass
public abstract class EntityBase implements Serializable {
But it is not mapped anywhere in the bounds.
EDIT3: A big thank you to #Radiodef! The theoretic solution ended up to be the following, and would work perfectly fine:
public interface ResultServiceHolder<M, ID extends Serializable, BO extends BusinessObject<M, ID>> {
ResultService<M, ID, BO> getService();
}
public abstract class ResultTypes<M, ID extends Serializable, BO extends BusinessObject<M, ID>>
implements ResultServiceHolder<M, ID, BO> {
public static ResultTypes<?, ?, ?>[] values() {
return new ResultTypes<?, ?, ?>[] {RESULT_ONE, RESULT_TWO, RESULT_THREE};
}
public static final ResultTypes<ResultOne, Long, ResultOneBO> RESULT_ONE = new ResultTypes<ResultOne, Long, ResultOneBO>("Result One") {
#Override
public ResultOneService getService() {
return serviceInitializer.resultOneService;
}
};
public static final ResultTypes<ResultTwo, Long, ResultTwoBO> RESULT_TWO = new ResultTypes<ResultTwo, Long, ResultTwoBO>("Result Two") {
#Override
public ResultTwoService getService() {
return serviceInitializer.resultTwoService;
}
};
public static final ResultTypes<ResultThree, Long, ResultThreeBO> RESULT_THREE = new ResultTypes<ResultThree, Long, ResultThreeBO>("Result Three") {
#Override
public ResultThreeService getService() {
return serviceInitializer.resultThreeService;
}
};
protected String name;
protected ServiceInitializer serviceInitializer;
private ResultTypes(String name) {
this.name = name;
}
protected void setServiceInitializer(ServiceInitializer serviceInitializer) {
this.serviceInitializer = serviceInitializer;
}
#Component
static class ServiceInitializer {
#Autowired
private ResultOneService resultOneService;
#Autowired
private ResultTwoService resultTwoService;
#Autowired
private ResultThreeService resultThreeService;
#PostConstruct
public void init() {
for (ResultTypes resultType : ResultTypes.values()) {
resultType.setServiceInitializer(this);
}
}
}
}
I think because of how lengthy the solution becomes, I'll stick with the enum approach, and just accept this loss of bounds. I lose more by having to add my own values() implementation than I gain from enforcing these bounds. However, this is an interesting theoretical exercise, and thank you again for your help.
Okay, first you need to understand why what you're doing is probably not what you think it's doing. Let's look at a simpler example.
interface Face {
<T> List<T> get();
}
What you have there is a generic method, get. A generic method's type parameter depends on what is supplied by the call site. So for example like this:
Face f = ...;
// this call site dictates T to be Number
List<Number> l = f.<Number>get();
When you override it like
class Impl implements Face {
#Override
public List<String> get() { return ...; }
}
This is something you are able to do (only because of erasure) but you probably shouldn't. It's only allowed for backwards compatibility to non-generic code. You should listen to the warning and not do it. Doing it means that for example I can still come along and dictate it to return something else:
Face f = new Impl();
// now I've caused heap pollution because you
// actually returned to me a List<String>
List<Number> l = f.<Number>get();
This is why there is an unchecked conversion.
What you probably meant is to use a generic interface declaration:
interface Face<T> {
List<T> get();
}
Now the argument to T depends on the type of the object reference.
Face<Number> f = ...;
// get must return List<Number>
List<Number> l = f.get();
We can implement it like
class Impl implements Face<String> {
#Override
public List<String> get() { return ...; }
}
Additionally, you cannot access covariant return types on an enum. When you override methods on an enum constant, its class is anonymous. An anonymous class has no name and cannot be referred to. Therefore the programmer cannot know its covariant return type to use it. Furthermore, an enum cannot declare generic type parameters. So what you are wanting to do is simply impossible with enum.
You can use a class with public static final instances to simulate a generic enum:
public abstract class SimEnum<T> implements Face<T> {
public static final SimEnum<Number> A = new SimEnum<Number>() {
#Override
public List<Number> get() { return ...; }
};
public static final SimEnum<String> B = new SimEnum<String>() {
#Override
public List<String> get() { return ...; }
};
private SimEnum() {}
public static SumEnum<?>[] values() {
return new SimEnum<?>[] { A, B };
}
}
Otherwise you need to drastically change your idea.
Maybe use an interface/abstract class instead of an enum?
Enums cannot have type parameters but classes and interfaces can.
For example...
Interfaces
Entity.java
The "thing" interface...
import java.io.Serializable;
public interface Entity<K extends Serializable> {
// TODO: Put entity type things here!
// for example, things like "K getId();"
// You may want an abstract base class for this interface that all Entitys extend
}
Repository.java
Does CRUD stuff with things...
import java.io.Serializable;
public interface Repository<K extends Serializable, V extends Entity<K>> {
V getValue(K key);
// Other CRUD stuff
}
Service.java
A Service is responsible for doing stuff with things...
public interface Service<K, V> {
// Could have an abstract service class that has a repository and implements this for you...
V get(K key);
// Other "generic service" type stuff
}
Solid Classes
Entity1.java
Solid base class with String key...
public class Entity1 implements Entity<String> {
// TODO implement Entity stuff...
}
Entity2.java
Solid base class with Integer key...
public class Entity2 implements Entity<Integer> {
// TODO implement methods...
}
Entity1Service.java
Solid Entity1 Service
public class Entity1Service implements Service<String, Entity1> {
// Would not have to implement this if you extended an abstract base Service class
#Override
public Entity1 get(String key) {
return null;
}
}
Entity2Service.java
Solid Entity2 Service
public class Entity2Service implements Service<Integer, Entity2> {
// Wouldn't need this if you had abstract Service class either...
#Override
public Entity2 get(Integer key) {
return null;
}
}
ServiceHolder.java
Not an enum, but an interface - you could add methods to set the "service" from spring or something here...
import java.io.Serializable;
public abstract class ServiceHolder<K extends Serializable, V, S extends Service<K, V>> {
public static final ServiceHolder<String, Entity1, Entity1Service> ENTITY_1_SERVICE = new ServiceHolder<String, Entity1, Entity1Service>() {};
public static final ServiceHolder<Integer, Entity2, Entity2Service> ENTITY_2_SERVICE = new ServiceHolder<Integer, Entity2, Entity2Service>() {};
private S service;
private ServiceHolder() {
}
public S getService() {
return service;
}
public void setService(S service) {
this.service = service;
}
}
The interesting bit
I think this is the sort of thing you wanted, please let me know if I misunderstood...
public class PleaseCompile {
public static void main(String[] args) {
Entity1 solid1 = ServiceHolder.ENTITY_1_SERVICE.getService().get("[KEY]");
Entity2 solid2 = ServiceHolder.ENTITY_2_SERVICE.getService().get(42);
...
}
}
Hope this helps...
You cannot do what you want to do.
List<String> and List<Integer> face type erasure at runtime.
And so do your enum-mapped getService() functions.
Everything related to types for generics is validated at compile-time.

Categories