Unexpected behaviour with generics - java

I'm facing this error while work with Java generics, and I don't understand which is the problem;
I have two classes like these:
public abstract class BaseClass{
....
}
public class OneClass extends BaseClass{
....
}
I have a generic repo for OneClass:
public class MyRepo<T extends BaseClass>{
List<T> getElements();
}
Then I have a method that should works whit generics:
private MyRepo<OneClass> myRepo;
public <T extends BaseClass> List<T> myMethod(){
return myRepo.getElements();
}
The method doesn't work unless I force a cast to List ( as shown below ):
public <T extends BaseClass> List<T> myMethod(){
return (List<T>) myRepo.getElements();
}
I don't understand which is the problem given that OneClass extends BaseClass.
Thanks for any suggestion.

Having a method of the form:
<T> T myMethod()
makes the inference of the actual T dependent on the call-site:
String s = myMethod();
Integer i = myMethod();
Considering your scenario one could invoke your method like this:
List<BaseClass> a = myMethod();
List<OneClass> a = myMethod();
As you can see this can be incorrect as myMethod could actually return another subtype of BaseClass (lets say TwoClass) which is not correct to cast to List<OneClass> - thus you need the unsafe cast to List<T>.
You should change the signature of myMethod to one of the following:
public List<? extends BaseClass> myMethod(){}
public List<BaseClass> myMethod(){}
The first variant states that this is a list of any subtype of BaseClass the other just omits that information.
Dependent on what you want to achieve check the other answer or read about PECS (Producer Extends, Consumer Super) or f-bounded polymorphism / self-types to return the concrete type.

Error message tells you that not every T extends BaseClass is OneClass

You should make sure that the field myRepo is the same type as T as in your method. If you force it to be OneClass you cant use othere types except OneClass. So there is no use of a generic. If you want to allow every extending class from BaseClass you could make the class of the mehtod generic in oder to use the same type of T as shown below:
public class FunctionClass<T extends BaseClass> {
private MyRepo<T> myRepo;
public List<T> myMethod(){
return myRepo.getElements();
}
}

Related

Return ? extends Type

I want to have a method signature that looks as such:
public <? extends IDto> convertToResponseDto();
I came up with this from a valid signature that looks like:
public List<? extends IDto> convertToResponseDto();
As it turns out Type <? extends IDto> is not a valid return type. I feel like the intention of what I am trying to return is quite clear, but not sure what the correct syntax is
EDIT
This question was answered by #ErwinBolwidt, but gives a new problem in a use case. I have a case where I am using generics:
protected final <E extends IDto> E findOneInternal(final Long id) {
return getService().findOne(id).convertToResponseDto();
}
In this case, convertToResponseDto() returns IDto, and not the concrete class, as its not known at this pint because it gets the service of type T
You can override an interface method or superclass method with a more specific subtype as the return type. Say your superclass says
public IDto convertToDTO()
Then your subclass can say
public MySpecificDTO convertToDTO()
That's implied by the Java language and doesn't need any generics.
You can define a generic type
public <T extends IDto> T convertToResponseDto();

Incompatible classes of java generics

It seems I'm stuck with java generics again. Here is what I have:
Couple of classes:
class CoolIndex implements EntityIndex<CoolEntity>
class CoolEntity extends BaseEntity
Enum using classes above:
enum Entities {
COOL_ENTITY {
#Override
public <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls() {
return CoolIndex.class;
}
#Override
public <E extends BaseEntity> Class<E> getEntityCls() {
return CoolEntity.class;
}
}
public abstract <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls();
public abstract <E extends BaseEntity> Class<E> getEntityCls();
}
Function I need to call with use of result of getIndexCls() function call:
static <E extends BaseEntity, I extends EntityIndex<E>> boolean isSomeIndexViewable(Class<I> cls)
The problem is that compiler complains about return CoolIndex.class; and return CoolEntity.class; and it's not clear to me why... Of course I can cast it to Class<I> (first case) but it seems to me like I'm trying to mask my misunderstanding and it doesn't feel right.
The problem with getIndexCls is that because it's generic, the type parameters can be interpreted to be any classes that fit the bounds on the declarations. You may think that CoolIndex.class fits those bounds, and it does, but a caller of the method can supply their own type arguments which would be incompatible, e.g.:
Entities.COOL_ENTITY.<UncoolEntity, UncoolIndex>getIndexCls();
That would break type safety, so the compiler disallows this. You can cast to Class<I>, but the compiler will warn you about an unchecked cast for the same reason. It will compile, but it can cause runtime problems as I've described.
Other situations can resolve such a situation by passing a Class<I> object to make the type inference work properly, but that defeats the point of this method -- returning a Class<I> object.
Other situations call for moving the generic type parameters from the method to the class, but you are using enums, which can't be generic.
The only way I've come up with to get something similar to compile is by removing the enum entirely. Use an abstract class so you can declare class-level type parameters. Instantiate constants with the type arguments you desire.
abstract class Entities<E extends BaseEntity, I extends EntityIndex<E>> {
public static final Entities<CoolEntity, CoolIndex> COOL_ENTITY = new Entities<CoolEntity, CoolIndex>() {
#Override
public Class<CoolIndex> getIndexCls() {
return CoolIndex.class;
}
#Override
public Class<CoolEntity> getEntityCls() {
return CoolEntity.class;
}
};
// Don't instantiate outside this class!
private Entities() {}
public abstract Class<I> getIndexCls();
public abstract Class<E> getEntityCls();
}
This can be reproduced by much simpler example:
public <E extends BaseEntity> E get() {
return new BaseEntity(); // compilation error here
}
The problem in such declaration <E extends BaseEntity> is that your method claims to return an instance of any type E that caller should ask:
MyCoolEntity1 e = get(); // valid, E is MyCoolEntity1
MyCoolEntity2 e = get(); // valid, E is MyCoolEntity2
This code should be compile-time safe, so you have to cast result of your method to E
public <E extends BaseEntity> E get() {
return (E) new BaseEntity(); // no error, but unsafe warning
}
In your example it's pretty the same, you claim to return value of type Class<E>:
public <E extends BaseEntity> Class<E> getEntityCls()
But return a concrete class SomeEntity.class which is Class<CoolEntity>
OK, how should I fix that?
You can add type cast return (Class<I>) CoolIndex.class; / return (Class<E>) CoolEntity.class;
You can replace enum with classes, since enums can not be generic and classes can
You can entirely remove generics, since there's no much value in it

Intertwined java generic interfaces and classes

I have a very specific problem with java generics. The follwowing classes and interfaces have been predefined:
public interface IFirst<R, T> {...}
public abstract class AbstractFirst<T extends AbstractFirst, L extends IFirst<String, T>> {...}
public interface ISecond extends IFirst<String, AbstractSecond> {...}
public abstract class AbstractSecond extends AbstractFirst<AbstractSecond, ISecond> {...}
Now I've created a following repo definition which seems to be valid:
public abstract class AbstractRepo<T extends AbstractFirst<T, IFirst<String,T>>> {...}
But now that i want to extend it:
public class RepoFirst extends AbstractRepo<AbstractSecond> {...}
I get the following error:
Bound mismatch: The type AbstractSecond is not a valid substitute for the bounded parameter
<T extends AbstractFirst<T,IFirst<String,T>>> of the type AbstractRepo<T>
I cannot change the first four (at least not radically) beacuse they are too heavily ingrained with the rest of the application, but the second two are new and up for change if need be.
Also intrestingly it allows the following (with raw type warnings):
public class RepoFirst extends AbstractRepo {
...
#Override
AbstractFirst someAbstractMethod() {
return new AbstractSecond() {...};
}
...
}
But for code clarity I would like to implement it with clearly defining AbstractSecond as the generic type for Abstract Repo.
What am I missing?
Your AbstractRepo expects an instance of IFirst and not a subtype of IFirst. But your AbstractSecond is clearly not IFirst. (I mean it is, from a OO standpoint but for generics, List<Number> is not the same as List<Integer>). It's ISecond. It might work if you could change your AbstractRepo from IFirst to ? extends IFirst as you did for AbstractFirst.

How to return a subclass of parameterized return type whose parametric type is a subclass of the return type's parametric type?

The title sounds a bit complicated, but in fact the question is quite simple.
I have the following method:
AbstractClassA<AbstractClassB> myMethod() {...}
And I want to invoke it like this:
ClassAImpl<ClassBImpl> result = myMethod();
The problem is that no matter what I tried, I ended up with unchecked cast warning or compiler error.
What is the right way of doing it?
It all depends. The simplest answer is that you're just going to need to cast the results of myMethod to the same type as result. Suppose we take all the generics out of the problem, like this:
public class Demo {
private abstract class AbstractClassA {}
private class ClassAImpl extends AbstractClassA {}
private abstract class AbstractClassB {}
private class ClassBImpl extends AbstractClassB {}
Demo() {
ClassAImpl result = myMethod();
}
public AbstractClassA myMethod() {
return new ClassAImpl();
}
public static void main(String[] args) {
new Demo();
}
}
The call to myMethod is still a compiler error, because the types don't match. A good way to think about this is to imagine that you don't know anything about the code in the body of myMethod. How would you know that it's returning a ClassAImpl and not some other subtype of AbstractClassA? So to make it work, you'd need to invoke myMethod like this:
ClassAImpl result = (ClassAImpl)myMethod()
As for the generics bit, you probably want your method signature to be something like:
public AbstractClassA<? extends AbstractClassB> myMethod()
but maybe want something like:
public AbstractClassA<ClassBImpl> myMethod()
or even:
public ClassAImpl<ClassBImpl> myMethod()
All except the last are going to require some explicit casting.
Why on earth would you do something like that anyway? Methinks either your design is wrong or you are leaving out some important information. If myMethod is in the same class as the attempted call, so that your caller is privy to information about how the method is implemented, you should create a private myMethodAux that returns the more specific type. You are probably still going to run into variance problems, however, as ClassAImpl<ClassBImpl> is not a subclass of AbstractClassA<AbstractClassB>. It is a subclass of AbstractClassA<? extends AbstractClassB>.
Your comment above seems to indicate that you were not aware of this variance restriction on the subtyping relation in Java.
You can't assign a super type into sub type without explicit type casting.
Use instanceof operator before down casting.
Try Class#isInstance()
abstract class AbstractClassA<T extends AbstractClassB> {}
abstract class AbstractClassB {}
class ClassAImpl<T extends AbstractClassB> extends AbstractClassA<T> {}
class ClassBImpl extends AbstractClassB {}
...
public static AbstractClassA<? extends AbstractClassB> myMethod() {
return new ClassAImpl<ClassBImpl>();
}
...
AbstractClassA<? extends AbstractClassB> returnValue=myMethod();
if(returnValue.getClass().isInstance(new ClassAImpl<ClassBImpl>())){
ClassAImpl<ClassBImpl> result=(ClassAImpl<ClassBImpl>)returnValue;
...
}

An interface has two type parameters. Can I implement the interface with the two types being the same, such that they are then compatible?

This is an existing interface:
public interface MyInterface<T, U> {
public T foo(U u);
}
I want to implement this interface under the assumption that T and U are the same type. I thought maybe I could leave the type parameters in as they are, and then as long as I only ever instantiate this particular implementation with two of the same type, that it might work:
public class MyOuterClass<A> {
public class MyClass<T, U> implements MyInterface<T, U> {
#Override
public T foo(U u) {
return u; //error here
}
//even though in the only instantiation of MyClass, T and U are the same
private MyClass<A, A> myInstance = new MyClass<A, A>();
}
But, perhaps unsurprisingly, this doesn't work, as types T and U are incompatible.
So then I thought maybe I could change MyClass to specify that its types would always be the same, by changing it to something like MyClass<A, A> implements MyInterface<A, A> or similar, but I get errors saying that T is already defined.
Is there a way to implement MyClass so that its two types will be the same?
(I'm more of a C++ guy than Java, so sorry if I'm missing something fundamental about Java's generic's here.)
Your myclass needs to look like this:
public class MyClass<T> implements MyInterface<T, T> {
#Override
public T foo(T in) {
return in;
}
}
Let's review what your suggested class definition does:
public class MyClass<T, U> implements MyInterface<T, U>
In this code, T and U do two things each:
in the first occurance they define a type variable of your MyClass class
in the second occurance they specify the concrete type of the MyInterface class
Since inside the body of your class T and U are unbounded type variables (i.e. nothing is known about the actual types), they are assumed to be incompatible.
By having only a single type variable in your MyClass you make your assumption explicit: there's only a single type, and I'm using it for both types of the interface.
Last but not least: remember that the compilation of a type is complete once the source is fully handled. In other words: contrary to what C++ does, "instantiation" of a generic type ("template type" or similar in C++; Sorry for my rusty terminology) does not handle. MyClass<Foo> and MyClass<Bar> are the same type, as far as the JVM is concerned (only the compiler actually distinguishes them).
Define a single type parameter for MyClass:
class MyOuterClass<A> {
public class MyClass<T> implements MyInterface<T, T> {
public T foo(T u) {
return u;
}
}
// Need only one 'A' here.
private MyClass<A> myInstance = new MyClass<A>();
}
When you say
public class MyClass<T> implements MyInterface<T, T> {
... you are defining one generic variable for MyClass and you are saying that it fulfills both the roles T and U in MyInterface.

Categories

Resources