I'm trying to implement a Strategy + Factory pattern using generics. The goal is to return to a client class an implementation of the interface DocumentDao that can deal with a type T extends Document, so I've multiple Dao interface extending DocumentDao for different subtypes of Document.
Here is my code:
public class Document { ... }
public class DocumentA extends Document { ... }
public class DocumentB extends Document { ... }
public interface DocumentDao<T extends Document> {
public void update(T document);
}
public interface DocumentADao<DocumentA> {}
public interface DocumentDaoFactory {
public <T extends Document> DocumentDao<T> getDaoInstance(Class<T> clazz);
}
Then I try to use the Factory:
private <T extends Document> void someMethod(T document) {
...
DocumentDao<T> documentDao = this.documentDaoFactory.getDaoInstance(document.getClass());
documentDao.update(document);
...
}
But the compiler complaints about the getDaoInstance() call:
Type mismatch: cannot convert from DocumentDao<? extends AbstractGDriveDocument<?>> to DocumentDao<T>
How to deal with this situation?
How can I obtain a similar solution?
Thanks
The problem is that getClass is returning a Class<?>, which is appropriate for the API; it would not know what specific Class instance to bring back. Additionally, your type bound is incorrect and invalid in your method.
To fix this, you would need to change two things:
In your DocumentDaoFactory method, change the bound to be appropriate.
<T extends Document> DocumentDao<T> getDaoInstance(Class<T> clazz);
In your use of getDaoInstance, perform an unchecked cast to Class<T>.
DocumentDao<T> documentDao = this.documentDaoFactory.getDaoInstance((Class<T>) document.getClass());
The way that your types are bound should give you back the instances you care about, without getting any runtime errors.
Related
I have an interface that'ss defined as follows:
public interface MyApi<T extends Identity>
And a method read:
List<ReadApiResponse<? extends T>> read(ReadApiRequest request);
In my calling class I have a private method that should operate on the read result and its signature is:
public MyClass<T extends Identity> {
private List<T> sortResults(List<ReadApiResponse<? extends Identity>> response) {
// Do something here
}
}
I try to call the private method:
var results = read(request);
List<T> sortedResults = sortResults(results);
But I get the message:
'sortResults(java.util.List<com.mypackage.ReadApiResponse<? extends com.mypackage.Identity>>)' in '...' cannot be applied to '(java.util.List<com.mypackage.model.ReadApiResponse<? extends capture<? extends com.mypackage.Identity>>>)'
What am I doing wrong here? I tried to cast it with Intellij's problem-solver suggestions and it also throws an error
If you change the wildcard generic type constraints like follows, it should be easier to work with:
public interface MyApi<T extends Identity> {
List<ReadApiResponse<T>> read(ReadApiRequest request);
}
public class MyClass<T extends Identity> {
private List<T> sortResults(List<ReadApiResponse<T>> response) {
// Do something here
}
}
Note that, depending on the usage and the type Identity, this might eliminate some flexibility that you get by using wildcard type constraints. Without more context, it's impossible to say for sure if that will matter.
Tables:
StudentHistory 1--->n Student
TeacherHistory 1--->n Teacher
I try to regroup the JPA behaviour of History because they do the same thing (retrieve the students/teacher from a given history for example).
Entities with generic types:
// Entities
public abstract class AbstractHistory <T> {}
public class StudentHistory extends AbstractHistory<Student> {}
public class TeacherHistory extends AbstractHistory<Teacher> {}
Repositories with genric types:
// repositories
public interface IHistoryRepository<T> extends CrudRepository<AbstractHistory<T>, Long> {
public AbstractHistory<T> findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher> {}
I though I could do:
StudentHistory stuHisto = new StudentHistoryRepository().findFirst();
But I get this error:
// err -> Type mismatch: cannot convert from AbstractHistory<Student> to StudentHistory
1/ Why can't I retrieve a 'StudentHistory' from my 'StudentHistoryRepository' ?
2/ How should I deal whith that?
You have this problem because your method explicitly returns an AbstractHistory and not the subtype.
You would need to cast...
... if only your repository implementation understood that each T you get a specific history.
You may try adding another type but I fear that it'll fail:
public interface IHistoryRepository<
T,
H extends AbstractHistory<T>
> extends CrudRepository<H, Long> {
public H findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student, StudentHistory> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher, TeacherHistory> {}
I don't know what framework you are using, probably Spring Data from the names; while I had used it in the past, I don't know if it is able to do that.
After all, it needs to get the concrete class and since it is generics, type erasure may interfere (if the information about the concrete type representing H is lost in reflection then Spring Data won't probably be able to do much here, unless you help it with an annotation or something else).
Another solution that should work is to do that per each child interface instead:
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long> {
StudentHistory findFirst();
}
Or with another interface:
public interface FindFirst<T> {
T findFirst();
}
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long>, FindFirst<StudentHistory> {}
I've a structure like this:
abstract class MyDomain{...}
abstract class FooDomain extends MyDomain{...}
abstract class BarDomain extends MyDomain{...}
class FirstConcreteBarDomain extends BarDomain{...}
class SecondConcreteBarDomain extends BarDomain{...}
I need a factory that creates MyDomain objects. My first attempt was this:
public interface ISpecializedObjectsFactory {
public <T extends MyDomain> T create(Class<?> clazz);
}
Implementend as:
public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory {
#Override
public <T extends MyDomain> T create(Class<?> clazz) {
if(clazz.equals(BarDomain.class))
return new FirstBarDomain();
throw new InvalidParameterException();
}
Same for the SecondBarDomain.
FIRST QUESTION: Why this is generating an error that says that it cannot cast FirstBarDomain to T?
After this error I've introduced a cast: return (T) new FirstBarDomain();.
The problem is that the cast is unsafe and I want to be confident for the result, so I've introduced another constraint (assuming that each MyDomain object have always 2 levels of derivation):
public <T extends AnagrafeDomain, S extends T> S create(Class<T> clazz)
SECOND QUESTION: Assuming that this factory is the only entry point where MyDomain objects are created, and that the calls to the factory never use the concrete classes (but are always like: BarDomain subj = SpecializedObjectsFactory.getFactory().create(BarDomain.class);), the question is: is this new version safe?
The reason the cast is unsafe is because of this particular line:
public <T extends MyDomain> T create(Class<?> clazz) {
This infers the return type from the call site; in other words, consider the following class:
public abstract class MyFakeDomain extends MyDomain { }
The following code will then compile, but fail at runtime:
ISpecializedObjectsFactory factory = new FirstSpecializedObjectsFactory();
MyFakeDomain broken = factory.create(BarDomain.class);
This will throw a ClassCastException because of the type inference; the inferred type will be MyFakeDomain, resulting in an attempt to cast FirstBarDomain to MyFakeDomain, which is an illegal cast - hence the unsafe warning.
The type inference is also the reason why the cast must be present; whilst FirstBarDomain is definitely a subclass of MyDomain, we do not know if it is of type T, as T could be any MyDomain subclass, not necessarily FirstBarDomain.
However, if the caller is careful, your code will work fine - whether you consider this acceptable or not is up to you.
This gives us the answer to your second question: using BarDomain as the type to be inferred will not always be safe, as it could be another subclass of MyDomain. The only type that would be always safe here is MyDomain - however, if you are planning on only using MyDomain as the type, you might as well remove the generic type bound and just make the return type MyDomain.
The constraint that will give you the confidence you are looking for is limiting the classes that your factory receives:
public interface ISpecializedObjectsFactory {
public <T extends MyDomain> T create(Class<? extends MyDomain> clazz);
}
public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory {
#Override
public <T extends MyDomain> T create(Class<? extends MyDomain> clazz) {
if(clazz.equals(BarDomain.class))
return (T) new FirstBarDomain();
throw new InvalidParameterException();
}
}
The compiler will not accept any call to create when the argument is not a subclass of MyDomain. However, it will accept an abstract class. If you want to know you received a concrete class, you can find the answer here How can I determine whether a Java class is abstract by reflection
I have the following 2 interfaces accordingly to abstract factory pattern:
public interface GenericObjectInterface<T extends Number>{
public T getResult();
}
public interface AbstractFactoryInterface{
public <T extends Number> GenericObjectInterface<T> createGenericObject();
}
I have an abstract class implementing GenericObject, but it's still unaware of the concrete type (it does only generic operations on Number):
public abstract class GenericAbstractClass<T extends Number> implements GenericObjectInterface<T>{ }
Then I have a series of concrete class extending that perform generic parameter substitution:
public class IntegerObject extends GenericAbstractClass<Integer>{
public Integer getResult(){}
}
....
Now, from inside an implementation of the factory I build the concrete type, that's implementing GenericObjectInterface but has lost it's generic parameter:
public class ConcreteFactory{
public <T extends Number> GenericObjectInterface<T> greateGenericObject(Class<T> c){
if (c.class.isInstance(Integer.class)){
IntegerObject obj = new IntegerObject();
//I would like to return obj
GenericObjectInterface<T> a = new IntegerObject(); //errror
GenericAbstractClass<T> a = new IntegerObject(); //errror
return a;
}else if (c.class.isInstance(Double.class)){
}
}
}
I would like to return obj that implements GenericObjectInterface but I don't know how can I do it.
how can I solve this?
I'm used to abstract factory but I've never used it with generics. Am I doing some mistakes in interpreting the pattern?
If your method returns an IntegerObject why don't you just return GenericObjectInterface<Integer>? You already know the parameter type.
In that case, just add a generic parameter to AbstractFactoryInterface, too:
public interface AbstractFactoryInterface<T extends Number> { ... }
public class ConcreteFactory implements AbstractFactoryInterface<Integer> { ... }
In your implementation the type of T would be inferred from the assignment, and thus you could do this:
GenericObjectInterface<Double> g = new ConcreteFactory().greateGenericObject();
In that case T would be Double but you'd use Integer internally, resulting in this:
GenericObjectInterface<Double> a = new IntegerCell();
Since the compiler can't ensure that T will always be of type Integer it won't allow you to do that assignment.
Abstract factory is characterized by the factory method returning an interface or abstract class reference instead of the concrete reference. It does not extend to type parameters.
Think of it this way: should you be able to do this?
public class ConcreteListFactory {
public <T> List<T> createList() {
return new ArrayList<String>();
}
}
What if the caller wanted a List<Integer>?
If you want your factory to return a generified type, you should have your concrete class accept the type parameter. Otherwise have your factory method return a GenericObjectInterface<Integer>.
Alternatively, you could have your method accept a type token (Integer.class). For example:
public <T extends Number> GenericObjectInterface<T> createGenericObject(Class<T> clazz) {
if ( clazz.equals(Integer.class) ) {
return (GenericObjectInterface<T>) new IntegerObject();
}
}
This will result in an unchecked cast warning but you can prove to yourself that it is safe, and thus suppress the warning or ignore it.
Generally, factories are not implemented as generics because you can't examine the type of the generic to determine the type of object to create (you can't do T.getClass) which is why #Mark's example causes the class to be passed in as an argument.
I think, more usually you would have multiple concrete factories. One for each Number type that you intend to support.
public interface AbstractFactoryInterface<T extends Number> {
public GenericObjectInterface<T> createGenericObject();
}
class IntegerFactory implements AbstractFactoryInterface<Integer>...
class LongFactory implements AbstractFactoryInterface<Long>...
You could then create a Map<Class, AbstractFactoryInterface>...
Map<Class, AbstractFactoryInterface> myMap = ...;
myMap.put(Integer.class, new IntegerFactory());
myMap.put(Long.class, new LongFactory ());
casting is perfectly fine here. if c==Integer.class, then T=Integer, casting GOI<Object> to GOI<T> is absolutely correct. It is a checked cast because you have checked that T=Integer before casting, therefore the unchecked warning can be legitimately suppressed.
Just got a question about generics, why doesn't this compile when using a generic List? If its not possible, anyway around it? Much appreciate any answer.
// Interface used in the ServiceAsync inteface.
public interface BaseObject
{
public String getId();
}
// Class that implements the interface
public class _ModelDto implements BaseObject, IsSerializable
{
protected String id;
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
}
// Interface used in the ServiceAsync inteface.
public interface MyAsync<T>
{
// Nothing here.
}
// Service interface use both interfaces above.
public interface ServiceAsync
{
public void getList(MyAsync<List<? extends BaseObject>> callback);
}
public class MyClass
{
ServiceAsync service = (some implementation);
MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>()
{
};
service.getList(callBack); // This does not compile, says arguments are not applicable????
}
The fact that your MyAsync interface doesn't contain any method signatures and doesn't have a particularly informative name is a code smell from my perspective, but I'll assume that this is just a dummy example. As it is written, getList() couldn't ever have any reasonable implementation that used the callback in any way; remember that type erasure will erase this method signature to getList(MyAsync callback);
The reason that this doesn't compile is that your bound is wrong. MyAsync<List<? extends BaseObject>> gives T as List<? extends BaseObject>, a list of some unknown type.
It looks to me like what you want is for the getList method itself to be generic:
public interface ServiceAsync {
public <T extends BaseObject> void getList(MyAsync<List<T>> callback);
}
public class MyClass {
public void foo() {
ServiceAsync service = null;
MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>() {};
service.getList (callBack); // This compiles
}
}
The '?' in generic types can be pretty confusing. Honestly I'm not sure why this won't compile. It has to do with using the '?' in a nested generic type. But I do know some ways to work around it.
Is there a reason that the declaration of the MyAsync in MyClass has to reference _ModelDto? It would work if you changed it to look like this:
ServiceAsync service = (some implementation);
MyAsync<List<? extends BaseObject>> callBack = new MyAsync<List<? extends BaseObject>>()
{
};
service.getList(callBack);
If you need to reference the type _ModelDto directly you could change the definition of ServiceAsync and it will fix the problem.
Change it to look like this:
public interface ServiceAsync<T extends BaseObject>
{
public void getList(MyAsync<List<T>> callback);
}
Then add the parameter type to the declaration in MyClass
public class MyClass
{
public void method()
{
ServiceAsync<_ModelDto> service = (some implementation);
MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>()
{
};
service.getList(callBack);
}
}
This has got to do with the subtyping rules for parametrized types. I'll explain it in three steps:
Non-nested case
When you have the following subtype relation (where <: is the symbol for "is a subtype of"):
_ModelDto <: BaseObject
The following relation does not hold:
List<_ModelDto> <: List<BaseObject>
But the following relations do:
List<_ModelDto> <: List<? extends _ModelDto> <: List<? extends BaseObject>
This is the reason why Java has wildcards: to enable these kind of subtype relations. All of this is explained in the Generics tutorial. If you understand this, we can continue with the nested case...
Nested case
Let's do exactly the same, but with one more level of nesting. Starting from the subtype relation:
List<_ModelDto> <: List<? extends BaseObject>
The following relation does not hold, for exactly the same reasons as above:
MyAsync<List<_ModelDto>> <: MyAsync<List<? extends BaseObject>>
This is precisely the conversion you are trying to do when calling service.getList(callBack), and since the subtype relation does not hold, the conversion fails.
However, as above, you do have the following relations:
MyAsync<List<_ModelDto>>
<: MyAsync<? extends List<_ModelDto>>
<: MyAsync<? extends List<? extends BaseObject>>
Solution
So you should write the signature of getList as follows to make the call work:
public void getList(MyAsync<? extends List<? extends BaseObject>> callback);
The difference will be that the body of getList will be constrained with how it can use the callback. If MyAsync contains the following members:
public interface MyAsync<T> {
T get();
void set(T t);
}
Then, the body of getList will be able to get a list from the callback. However, it cannot set the list (except setting it to null), because it does not know exactly what kind of list is represented by the ?.
In contrast, with your original signature, set is available, and that is why the compiler cannot allow your argument.