I am no expert with generics and as I am trying to do a reengineering of some classes to avoid code repetition, I am forcing myself to use generics in order to do it the best way possible.
I am getting the next error in the lines I marked:
The method delete(Long) in the type CrudRepository is not applicable for the arguments (capture#5-of ? extends KeyProfileEntity)
Here my class:
public abstract class KeyProfileService {
protected CrudRepository<? extends KeyProfileEntity, Long> myRepository;
public List<KeyProfileEntity> getList() {
List<KeyProfileEntity> result = new ArrayList<>();
this.myRepository.findAll().forEach(result::add);
return result;
}
public KeyProfileEntity create(KeyProfileEntity entity) {
return this.myRepository.save(entity); //error
}
public boolean delete(long id) {
if (this.myRepository.exists(id)) {
this.myRepository.delete(this.myRepository.findOne(id));//error
return true;
}
return false;
}
public void update(KeyProfileEntity entity) {
this.myRepository.save(entity); //error
}
public KeyProfileEntity getEmployee(long id) throws NotFoundEntryException {
if (this.myRepository.exists(id))
return this.myRepository.findOne(id);
throw new NotFoundEntryException();
}
}
I think this is all the info you guys need, otherwise comment and I will attach more.
Thanks in advance!
You can fix it by removing the <? extends ...> bound wildcard from myRepository:
protected CrudRepository<KeyProfileEntity, Long> myRepository;
As far as I can see, your class will be still usable, even with subclasses of KeyProfileEntity:
KeyProfileService service = new KeyProfileServiceImpl();
service.update(new ChildKeyProfileEntity());
There will be only one limitation: getList() will always return a List<KeyProfileEntity>, not a List<ChildKeyProfileEntity>.
Alternatively, you can make the KeyProfileService generic and make sure you use a bound, known subtype:
public abstract class KeyProfileService<K extends KeyProfileEntity> {
protected CrudRepository<K, Long> myRepository;
public List<K> getList() { // using K
List<K> result = new ArrayList<>(); // here too
this.myRepository.findAll().forEach(result::add);
return result;
}
public K create(K entity) { // using K
return this.myRepository.save(entity);
}
...
}
Related
I want to collect a list of Processors who can operate on different parameters.
Here are some example classes
public abstract class AbstractHistory {
public String getTlp() { return ""; }
}
public class SynchResourcConfig {
public boolean isEnabled() { return true); }
}
public class SynchClassTlpFilterConfig extends SynchResourcConfig {
public String getClass() { return ""; }
}
This is the interface I want each element of the list to implement
public interface ConfigProcessingFilter {
public boolean shouldProcess(AbstractHistory history, SynchResourceConfig config);
}
This class ideally would hold all the processors in a list
The idea being, when 'shouldProcess()' gets invoked, it can reply with the result of any included processor.
public class ConfigProcessor {
protected List<ConfigProcessingFilter> filters = new ArrayList<>();
public boolean shouldProcess(AbstractHistory history, SynchResourceConfig config) {
return config.isEnabled() && filters.stream().anyMatch(
(filter) -> filter.shouldProcess(history, config));
}
public ConfigProcessor addFilter(ConfigProcessingFilter filter) {
filters.add(filter);
return this;
}
}
The dilemma
I want to allow for processing on subclasses of the data in the list as well. But I'm getting the error indicated in the constructor's comments.
public class ClassTlpProcessingFilter extends ConfigProcessor {
public ClassTlpProcessingFilter() {
/*
Compiler underlines: `processByClassTlp()` with error:
The method processByClassTlp(AbstractHistory, SynchClassTlpFilterConfig)
in the type ClassTlpProcessingFilter is not applicable
for the arguments (AbstractHistory, SynchResourceConfig)
*/
addFilter((history, config) -> processByClassTlp(history, config));
}
public boolean processByClassTlp(AbstractHistory history, SynchClassTlpFilterConfig config) {
return config.getClass().equals(history.getTlp());
}
}
Looking at the issue
The List contains ConfigProcessingFilter. Each of those items has a shouldProcess() method.
I was hoping this would allow any subclass of the described types to be accepted as parameters.But obviously that isn't working.
Is there anything that could be done to fix this?
Thanks in advance
I think I figured out how to do it. It seems to work if I propagate the elements' type all the way up from the lowest child class up to the parent class.
Interface updated with type params
public interface ConfigProcessingFilter<H extends AbstractHistory, C extends SynchResourceConfig> {
public boolean shouldProcess(H history, C config);
}
Processor updated to operate on generic types
public class ConfigProcessor<H extends AbstractHistory, C extends SynchResourceConfig> implements ConfigProcessingFilter<H, C> {
private List<ConfigProcessingFilter<H, C>> filters = new ArrayList<>();
public boolean shouldProcess(H history, C config) {
return config.isEnabled() && filters.stream().anyMatch(
(filter) -> filter.shouldProcess(history, config));
}
}
public ConfigProcessor addFilter(ConfigProcessingFilter<H, C> filter) {
filters.add(filter);
return this;
}
}
Update to the subclass
public class ClassTlpProcessingFilter<H extends AbstractHistory, C extends SynchClassTlpFilterConfig> extends ConfigProcessor<H, C> {
public ClassTlpProcessingFilter() {
addFilter((history, config) -> processByClassTlp(history, config));
}
public boolean processByClassTlp(AbstractHistory history, SynchClassTlpFilterConfig config) {
return config.getClass().equals(history.getTlp());
}
}
I'm learning about generics and I don't know how to resolve a problem.
This is the code:
public abstract class AbstractMapService<T, ID> {
protected Map<ID, T> map = new HashMap<>();
Set<T> findAll(){
return new HashSet<>(map.values());
}
T findById(ID id) {
return map.get(id);
}
T save(ID id, T object){
map.put(id, object);
return object;
}
void deleteById(ID id){
map.remove(id);
}
void delete(T object){
map.entrySet().removeIf(entry -> entry.getValue().equals(object));
}
private Long getNextId() {
return Collections.max(map.keySet()) + 1;
}
}
And this is the error:
max(java.util.Collection<? extends T>) in Collections cannot be applied to (java.util.Set<ID>)
reason: no instance of type variable(s) T exist so that ID conforms to Comparable<? super T>
Can someone explain to me why I get this error and how to resolve it? Thank you!
This error means the elements in the parameter Collection of method Collections.max should implement interface Comparable. So it can use the compareTo to find the max elements in the Collection.
You can make it compile by declaring it this way:
public abstract class AbstractMapService<T, ID extends Comparable<ID>>
private ID getNextId() {
return Collections.max(map.keySet());
}
but I do not think this make much sense.
You might want to re-consider your design. With your current code, the ID can be any type. For exmaple, it could be String. And this time, your getNextId should not return current maxID + 1, since the +1 is only make sense when your ID is a Number.
If ID is supposed to be Long, then you should not declare it as type parameter, you can write it this way:
public abstract class AbstractMapService<T> {
protected Map<Long, T> map = new HashMap<>();
Set<T> findAll(){
return new HashSet<>(map.values());
}
T findById(Long aLong) {
return map.get(aLong);
}
T save(Long aLong, T object){
map.put(aLong, object);
return object;
}
void deleteById(Long aLong){
map.remove(aLong);
}
void delete(T object){
map.entrySet().removeIf(entry -> entry.getValue().equals(object));
}
private Long getNextId() {
return Collections.max(map.keySet()) + 1L;
}
}
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);
}
}
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.
Consider the following simple code
import java.util.*;
public class MainTest<T extends Object1<?,?>> {
List<T> list;
public MainTest(List<T> l) {
this.list=l;
}
public int testCompare() {
// fails to compile here
return list.get(0).compareTo(list.get(1));
}
public static void main(String[]args) {
List<Object1Impl> list = new ArrayList<Object1Impl>();
list.add(new Object1Impl());
list.add(new Object1Impl());
MainTest<Object1Impl> test = new MainTest<Object1Impl>(list);
System.out.println(test.testCompare());
}
}
interface Object1<E, V> extends Comparable<Object1<E,V>> { }
class Object1Impl implements Object1<Integer, Integer>{
public int compareTo(Object1<Integer, Integer> o) { return 0; }
}
I am aware that in this case the program will not compile (fails at testCompare() because T is extending unbounded Object1<?,?>). Is there any alternative to fix this besides making MainTest<T extends Object1<E,V>,E,V>?
EDIT: the error message is
The method compareTo(Object1<capture#1-of ?,capture#2-of ?>) in the type Comparable<Object1<capture#1-of ?,capture#2-of ?>> is not applicable for the arguments (T)
I have read Effective Java book but still can't really think of a solution..
Also, why is it that if I change interface Object1 into an abstract class the program will compile without any problem? This really puzzles me...
EDIT: when I mean changing into abstract class is as follows
abstract class Object1<E, V> implements Comparable<Object1<E,V>>{
public int compareTo(Object1<E,V> o) { return 0; }
}
class Object1Impl extends Object1<Integer, Integer>{ }
this will work (only if using Eclipse, compiling it manually using javac does not work) but I have no idea why
This is correct; the compiler has no way to verify that list.get(0) and list.get(1) are of the same type; one might be Object1<String, Integer> and the other Object1<BigDecimal, Double>.
To make sure that they are of the same type, you would have to bind those types:
public class MainTest<A,B,T extends Object1<A,B>> {
List<T> list;
public MainTest(List<T> l) {
this.list=l;
}
public int testCompare() {
// fails to compile here
return list.get(0).compareTo(list.get(1));
}
public static void main(String[]args) {
List<Object1Impl> list = new ArrayList<Object1Impl>();
list.add(new Object1Impl());
list.add(new Object1Impl());
MainTest<Integer, Integer, Object1Impl> test = new MainTest<Integer, Integer, Object1Impl>(list);
System.out.println(test.testCompare());
}
}
As far as I know, Java doesn't allow binding parameter types to classes without specifically specifying them.