Incompatible classes of java generics - java

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

Related

Unexpected behaviour with generics

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();
}
}

Java: Method returns Class<T> (where <T extends BaseType>). Returning subclass class causes type mismatch error

Long time reader, first time caller (on this board anyway).
Envision the following:
public class BaseClass{ }
public class C1 extends BaseClass { }
public class C2 extends BaseClass { }
Now, I have a method:
private static <T extends BaseClass> Class<T> getBaseClassType(boolean b) {
if (b) {
return C1.class;
} else {
return C2.class;
}
}
I would think this is valid since both C1.class C2.class fulfill the type declaration <T extends BaseClass>.
However, the 'return' statements are tagged with "Type mismatch: cannot convert from Class<C1> to Class<T>".
Why is that? Can the method above be re-expressed in a manner to not require casts?
I am able to cast the returned values like this:
private static <T extends BaseClass> Class<T> getBaseClassType(boolean b) {
if (b) {
return (Class<T>) C1.class;
} else {
return (Class<T>) C2.class;
}
}
...but this generates warnings "Type safety: Unchecked cast from Class to Class" (which, of course, can be suppressed). I'm confused as to why this is necessary since C1 (or C2) extends BaseClass.
What's the "right" way to do this to make the editor/compiler happy?
thanks!
Turns out the solution is to remove the explicit type and substitute an unknown type specification:
private static Class<? extends BaseClass> getBaseClassType(boolean b) {
if (b) {
return (Class<T>) C1.class;
} else {
return (Class<T>) C2.class;
}
}
I found this after searching around some of the 'related' answers.
You have to adjust the class return type as shown below.
private static Class<? extends BaseClass> getBaseClassType(boolean b) {
if (b) {
return C1.class;
} else {
return C2.class;
}
}
The problem is that a cast is a runtime check and compile time its not known what would be type of T that why its raising warning to check the type of class before cast for safer side.
Use #SuppressWarnings("unchecked") to suppress warning -
#SuppressWarnings("unchecked")
Method signature
or add unknown type as
private static Class<? extends BaseClass> getBaseClassType(boolean b)

Generic methods returning a collection giving an Unsafe type cast warning

When I define a method to return a class of Type , shouldn't I be able to override the method with any type that extends Base? Take the following example:
MultiEntryModel.java (parent class)
protected abstract <T extends Model> List<T> getDataList();
ProgramData.java
public class ProgramData extends MultiEntryModel {
...
#Override
protected List<Program> getDataList() {
return programs;
}
}
Program.java
public class Program extends Model {
...
}
I'm getting an unsafe type cast warning in Programs.java, even though I'm returning a List containing objects of type Program, which extends the Model class, just as indicated by the method signature in the base class <T extends Model>. Why?
protected abstract <T extends Model> List<T> getDataList();
This means that a List will be returned which has to be safe for any T. For example:
List<SomeModel> someModelList = thing.getDataList();
If the list you're returning is a List<Program>, this is going to be unsafe - because you can add a SomeModel into that list, and/or you can try to get a SomeModel from the list. If Program is a subtype/supertype of Program, just one of those operations is safe; otherwise, they are both unsafe.
Don't define the type variable on the method - put it on the class:
abstract class TheClass<T extends Model> {
protected abstract List<T> getDataList();
}
class ProgramClass extends TheClass<Program> {
protected List<Program> getDataList() { return programs; }
}

Return generic class which extends Enum and implements and Interface-Java

I have an interface:
public MyInterface {};
And few enums which implement MyInterface as:
public enum MyFirstEnums implements MyInterface{};
In some another class, I need a method to return Class object such that
the Class should be like MyFirstEnums, which extends enum and implements MyInterface.
The return type of method should be: <E extends Enum<?> & MyInterface>, which should allow returning MyFirstEnums.class and like enums
I tried doing it like:
public <E extends Enum<?> & MyInterface> Class<E> getClazz(){
return MyFirstEnums.class;
}
but this give me error as:
Type mismatch: cannot convert from Class<MyFirstEnums> to Class<E>
What am I missing here?
At other place, I tried a method which takes such type of class as an argument and that worked fine:
public <E extends Enum<?> MyInterface> void doSomething(Class<E> myClazz){};
//it rightly checks the type of myClazz while calling this method
You can't just return the class object of MyFirstEnum. Your method is declared to return a class object that corresponds to an enum and implements MyInterface but it could be an enum other than MyFirstEnum that also meets this criteria. Imagine you have another enum:
public enum MySecondEnum implements MyInterface { }
You could also do:
Class<MySecondEnum> clazz = getClazz();
The compiler infers the type argument from the target return type of clazz (E is inferred as MySecondEnum). In this case, clearly a runtime exception is likely to occur. For example, when you try to instantiate an object from the returned class, you would get a java.lang.InstantiationException.
Note that your method does not actually use the type argument, so why have it generic in the first place?
If you want a "generic" way to return class objects for each enum implementing the interface, you can do something like:
abstract class EnumProvider<E extends Enum<?> & MyInterface> {
...
public abstract Class<E> getClazz();
}
class MyFirstEnumProvider extends EnumProvider<MyFirstEnums> {
...
#Override
public Class<MyFirstEnums> getClazz() {
return MyFirstEnums.class;
}
}
I would try some response:
You want to define a method which return some class type which is not fully defined, but which respects: extends Enum & MyInterface.
And your Class is not passed to the method.
I have a doubt it is not possible or safe.
I would :
1 pass a Class I want as an argument
2 inverse the test: I test inside if Class< E> extends Enum & MyInterface and MyFirstEnum.class and if MyFirstEnum.class extends class< E>
3 of course Class result=MyFirstEnum.class; can be more sophisticated and not known before...
This compiles (because tests are made at runtime) but I dont know if it can help you.
public <E> Class<E> getClazz(Class< E> _return_class) throws Exception
{
Class result=MyFirstEnum.class;
if (MyInterface.class.isAssignableFrom(_return_class) )
if (Enum.class.isAssignableFrom(_return_class))
if (_return_class.isAssignableFrom(result))
return (Class<E>) result;
throw new Exception("Bad Class");
}

Passing generic subtype class information to superclass in Java

I've long used an idiom in Java for using the class information of a (non-abstract) class in the methods of its (generally abstract) ancestor class(es) (unfortunately I can't find the name of this pattern):
public abstract class Abstract<T extends Abstract<T>> {
private final Class<T> subClass;
protected Abstract(Class<T> subClass) {
this.subClass = subClass;
}
protected T getSomethingElseWithSameType() {
....
}
}
An example of a subclass thereof:
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
However, I'm having trouble defining a subclass of Abstract which has its own generic parameters:
public class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super(Generic.class);
}
}
This example is not compilable; likewise, it is not possible to specify the generic types using e.g. Generic<T>.class or even to use a wildcard like Generic<?>.
I also tried replacing the declaration of generic type T in the superclass to ? extends T, but that isn't compilable either.
Is there any way I can get this pattern to work with generic base classes?
The "pattern" (idiom) of passing an instance of Class<T> (typically to the constructor) is using Class Literals as Runtime-Type Tokens, and is used to keep a runtime reference to the generic type, which is otherwise erased.
The solution is firstly to change the token class bound to:
Class<? extends T>
and then to put a similar requirement on your generic subclass as you did with your super class; have the concrete class pass a type token, but you can type it properly as a parameter:
These classes compile without casts or warnings:
public abstract class Abstract<T extends Abstract<T>> {
private final Class<? extends T> subClass;
protected Abstract(Class<? extends T> subClass) {
this.subClass = subClass;
}
}
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
public class Generic<T> extends Abstract<Generic<T>> {
public Generic(Class<? extends Generic<T>> clazz) {
super(clazz);
}
}
And finally at the concrete class, if you declare the usage as its own class, it doesn't require a cast anywhere:
public class IntegerGeneric extends Generic<Integer> {
public IntegerGeneric() {
super(IntegerGeneric.class);
}
}
I haven't figured out how to create an instance of Generic (anonymous or not) without a cast:
// can someone fill in the parameters without a cast?
new Generic<Integer>(???); // typed direct instance
new Generic<Integer>(???) { }; // anonymous
I don't think it's possible, but I welcome being shown otherwise.
The major problem you have got here is, there is no class literal for concrete parameterized type. And that makes sense, since parameterized types don't have any runtime type information. So, you can only have class literal with raw types, in this case Generic.class.
Reference:
Java Generics FAQs
Why is there no class literal for concrete parameterized types? :
Well, that's fine, but Generic.class gives you a Class<Generic> which is not compatible with Class<Generic<T>>. A workaround is to find a way to convert it to Class<Generic<T>>, but that too you can't do directly. You would have to add an intermediate cast to Class<?>, which represents the family of all the instantiation of Class. And then downcast to Class<Generic<T>>, which will remove the compiler error, though you will an unchecked cast warning. You can annotate the constructor with #SuppressWarnings("unchecked") to remove the warning.
class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super((Class<Generic<T>>)(Class<?>)Generic.class);
}
}
There is no need in Class<T> subClass argument. Change:
protected Abstract(Class<T> subClass) {
this.subClass = subClass;
}
to:
protected Abstract(Class subClass) {
this.subClass = subClass;
}
and everything will compile.

Categories

Resources