Java generics code cannot compile - java

I have the following code that give me trouble compiling.
Give me this error
Error:(20, 22) java: incompatible types: com.company.Main.Impl cannot be converted to T
I only need that interface to work in this function, and I don't want to change the function body much.
Why doesn't this work?
and
How could I make this work?
package com.company;
public class Main {
class Impl implements someinterface, anotherinterface{
#Override
public Integer getInteger() {
return 0;
}
}
class BigObject{
public Impl get(){
return new Impl();
}
}
private <T extends someinterface & anotherinterface> Integer myfunc(BigObject bg){
T xy = bg.get(); // this line will not compile????
if (xy.isOK()) // from someinterface
return xy.getInteger(); // from anotherinterface
return null;
}
public static void main(String[] args) {
// write your code here
}
}

It won't compile because in Java, generics are invariant, see related post.
With the following line of code:
<T extends SomeInterface> Integer myfunc(BigObject bg) { ... }
you are saying that T is something that is some kind of SomeInterface, or, more precisely, a certain type that is a subtype of SomeInterface. The compiler complains about T xy = bg.get(), because bg.get() returns a certain subtype of T, but that type may or may not be the same as Impl.
As an analogy, you are saying something like this:
class Cat extends Animal { }
class AnimalObj {
public Cat get() {
return new Cat();
}
}
private <T extends Animal> Integer myfunc(AnimalObj bg) {
T xy = bg.get();
...
}
T could be a Cat, but it could also be a Dog. Who knows. So that's why the compiler complains.
If you don't care about the subtype, you should drop generics, and write this instead:
private Integer myfunc(AnimalObj bg) {
Animal xy = bg.get();
...
}
Since myFunc accepts a BigObject, which is able to deliver a concrete Impl, you could just replace <T extends someinterface & anotherinterface> by Impl.
Read more
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
Generics – The Java Tutorials

Lets change the myfunc a little bit so that it will return the generic type T:
private static <T extends someinterface & anotherinterface> T myfunc(BigObject bg){
return bg.get();
}
Now you can call it like this:
Impl i1 = myfunc( new BigObject() );
and like this:
Impl2 i2 = myfunc( new BigObject() );
But if BigObject.get would have only returned Impl you would have error with second call that expected Impl2.

Related

Java generics type inference fails

I am trying to define a class which type could be a subtype of the given one if inferred but it doesn't seem to work with the default Java type inference mechanism and I do not understand why.
Here are some relevant pieces of code to illustrate the situation
public class ObjectChanged<T extends Something> implements TriggeringCondition<T> {
private final Class<? extends T> type;
private String featureName = null;
protected ObjectChanged(Class<? extends T> type) {
this.type = type;
}
public ObjectChanged<T> onFeature(String featureName) {
this.featureName = featureName;
return this;
}
public static <X extends Something> ObjectChanged<X> objectChanged(Class<? extends X> type) {
return new ObjectChanged<>(type);
}
}
Let's say I have one class called FastCar extending Car. I would like to build an object change for a FastCar, but to downcast it to TriggeringCondition<Car>.
If I write the following code it works as expected
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class);
}
But then if I call the onFeature(String) method it doesn't compile anymore and complains that my triggering condition if of type FastCar, which is not compatible with Car.
If now I define the objectChanged function like this
public static <X extends Something, Y extends X> ObjectChanged<X> objectChanged(Class<Y> type, Class<X> baseType) {
return new ObjectChanged<>(type);
}
Then I can use this code which resolves the problem
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class, Car.class).onFeature("something");
}
I also found out I can fix the previous build issue with this syntax, but it's quite ugly imo.
TriggeringCondition<Car> test() {
return ObjectChanged.<Car> objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
No.
If you don't want to use the type witness (<Car>), all you can do is to assign the objectChanged result to a variable, and then call onFeature on that variable.
TriggeringCondition<Car> test() {
TriggeringCondition<Car> tc = objectChanged(FastCar.class);
return tc.onFeature("test");
}
This is a problem which crops up a lot if you use Guava's Immutable*.Builders:
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("foo")
.build();
The type witness is needed here, otherwise the type of the Builder is inferred to be ImmutableList.Builder<Object>, because the type of the polyexpression is determined before the .add(String) call.
It's annoying, but that's the nature of the beast.
One thing you could do is to define a static upcast method:
static <T extends Something> ObjectChanged<T> upcast(ObjectChanged<? extends T> oc) {
ObjectChanged<T> result = new ObjectChanged<>(oc.type);
return result.onFeature("test");
}
Now you can invoke something like:
TriggeringCondition<Car> test() {
return upcast(objectChanged(FastCar.class).onFeature("test"));
}

Why does the compiler not enforce the return type value for a generic that extends an interface?

public class Test {
public static void main(String... args) {
ClassB clazzB1 = getClassA(); // compile error
ClassB clazzB2 = getInterfaceC1(); // compile error
ClassB clazzB3 = getInterfaceC2(); // no compile error
}
public static <T extends ClassA> T getClassA() {
return (T) new ClassA() {};
}
public static InterfaceC getInterfaceC1() {
return new InterfaceC() {};
}
public static <T extends InterfaceC> T getInterfaceC2() {
return (T) new InterfaceC() {};
}
private static abstract class ClassA {
}
private static class ClassB {
}
}
public interface InterfaceC {
}
Why does getInterfaceC2() not give a compile error when assigned to clazzB3? It seems like it should and is something that I would like to have happen.
You can always do:
static class InterestingType extends ClassB implements InterfaceC {
}
And then have :
public static <T extends InterfaceC> T getInterfaceC2() {
return (T)new InterestingType();
}
As such calling:
ClassB clazzB3 = getInterfaceC2();
would work. In your case it is obvious to you that it will break, but the compiler can't prove that (easily). As such, your example is allowed, but will fail at runtime.
EDIT
The compiler looks at this:
ClassB clazzB1 = getClassA();
and sees that getClassA() returns a T that is defined as T extends ClassA, where ClassA is a class. It also sees that this T is assigned to ClassB, where ClassB is a class also. So it has to create a certain type that will conform to both ClassA and ClassB, something like a theoretical :
T extends ClassA & ClassB
but that is an impossible type, since no one can do ... extends ClassA, ClassB (this is simply not allowed in java). So the compiler fails. As a matter of fact if you do :
javac --debug=verboseResolution=all Test
you will see in the output, something like:
error: incompatible types: inference variable T has incompatible upper bounds ClassB,ClassA
On the other hand, when the compiler looks at this : ClassB clazzB3 = getInterfaceC2();, it sees that getInterfaceC2() returns:
T extends InterfaceC
and the result is assigned to ClassB, so it ends up with may be this type:
T extends ClassB & InterfaceC
which is perfectly valid. There can be such a type. I've shown you that you can create one as InterestingType, but as Holger pointed out, you could pass such a theoretical type yourself, via:
ClassB clazzB3 = Test.<InterestingType>getInterfaceC2();
this is called specifying a "type witness".

Get list of Enums from generic class

I have a class that is paramaterised with an extend of Enum.
public class MyClass<EnumType extends Enum> {
public MyClass(){
Enum<?>[] enums = EnumType.getEnumConstants();
}
}
The line:
Enum<?>[] enums = EnumType.getEnumConstants()
fails to compile with "can not resolve method".
How can I get to the base type and get the enums?
OTOH, if I do the following it works ok:
public void setEnumType(Class <? extends Enum> clazz){
Enum<?>[] enums = clazz.getEnumConstants();
}
I can't pass this into the constructor as it is a custom view which is directly inserted in the parent.
Owing to erasure, you have to pass an instance of the enum class to the constructor:
public class MyClass<EnumType extends Enum<EnumType>> {
// ^ don't forget this
public MyClass(Class<EnumType> c){
Enum<?>[] enums = c.getEnumConstants();
}
}
MyClass<YourEnum> m = new MyClass<>(YourEnum.class);
Or, you could pass YourEnum.values() directly. The risk there is that a caller can pass any array, not necessarily one with all values, without duplicates, in the right order etc.

Java Generics of the form <T extends A<T>> and Inheritance

I have a tricky problem with Java generics, or maybe I can't see the wood for the trees.
I have three classes, A, B, and C that look as follows.
abstract class A<T extends A<T>> {
abstract T sefl();
};
abstract class B<T extends B<T>> extends A<T> {
};
class C extends B<C> {
#Override
C sefl() {
return this;
}
}
Later I have different versions of B's as well as different versions of C's. Furthermore, I have a function test that should accept a list of B's (or of one of its cousins). Or in general, it should accept any list of elements that inherit from A. Unfortunately, I need to know the type of the list's elements in the function's body, i.e. the uppermost type a.self can return (or the type T). Function test looks as follows:
static <T extends A<T>> void test(List<T> list) {
for (A<T> a : list) {
#SuppressWarnings("unused")
T t = a.sefl();
}
}
Now, calling test with a list of C's works.
List<C> cs = new LinkedList<C>();
test(cs);
But
List<B> bs = new LinkedList<B>();
test(bs);
results in a warning requiring a type parameter and
List<B<?>> bs = new LinkedList<B<?>>();
test(bs);
is not valid. Where is my mistake, or how can I create a list of B's that is accepted by function test?
Some words to the motivation behind this problem. The classes A, B, and C (or Animal, Mammal, and Cat) implement a tree-like data structure, where each class extends the structure with some properties. Typically, all super-classes are abstract and you can only create instances from leaf-classes, e.g. cat. Now, the difficulty is that the classes implement a copy-on-write policy (COW), i.e. modifying an object creates and returns a new instance of itself with the modified property.
For example, let’s say all animals have an age property. You can easily define this property in Animal, and you can provide a getter method to return the age.
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
};
However, how do you define the setter method? You can write it as follows:
public abstract Animal setAge();
This requires that (at least) each non-abstract element must implement the setter function. For example:
class Cat extends Mammal<C> {
#Override
public Animal setAge(int age) {
return new Cat(/* .. */);
}
}
Remember, as we implement a COW policy, we must create a new instance. So, in the setter function (e.g. implement in Cat) we return a new cat with the new age. Calling cat.setAge(4) on a Cat element returns a new Cat. Unfortunately, because of the type signature, we only now that we got an Animal returned from setAge, even if we call it on a Cat directly. The twist with the generics helps to reveal the concrete type when calling setAge. So, we can construct Animal like this:
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
public abstract T setAge();
};
And in Cat we can say:
class Cat extends Mammal<C> {
#Override
public Cat setAge(int age) {
return new Cat(/* .. */);
}
}
So, back to the problem. Your right, using List<? extends Animal<?>> as the type of the list works, but unfortunately, I need some way to know the type of the elements. Or more concrete: Function test must replace the old element with the new one. For example:
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
#SuppressWarnings("unused")
Animal<?> a = animal.setAge(4711);
list.add(a);
}
}
And unfortunately, the list extension list.add(a); ist the statement that doesn't work with this signature.
Well, they are two very different implementations:
class C ...
and
class B<T extends B<T>> ...
The class C doesn't declare any generic type.
Simple letters for class names are a bit confusing here, so let's do:
abstract class Animal<T extends Animal<T>> {
abstract T sefl();
};
abstract class Mammal<T extends Mammal<T>> extends Animal<T> {
};
class Cat extends Mammal<Cat> {
#Override
Cat sefl() {
return this;
}
}
So:
List<Cat> catList = new LinkedList<>();
works well, as there is no generic type involved. The compiler determines that
Cat extends Mammal<Cat> ( == Cat extends Animal<Cat> )
fits within the bounds <T extends Animal<T>>
On the other hand for
List<Mammal> mammalList = new LinkedList<>();
test(mammalList); // ok, but mammal list of what???
the compiler can't match the bounded types.
In fact, Mammal<T extends Mammal<T>> extends Animal<T> doesn't have anything to do with <T extends Animal<T>>.
Even by providing a wildcard, you'll never be able to pass a List<Mammal<?> to test. The method signature rejects it!
A possible solution:
A more generic test method
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
Animal a = animal.sefl();
}
}
can be used along with different List types:
List<? extends Mammal<?>> bs = new LinkedList<>();
test2(bs);
List<Cat> catList = new LinkedList<>();
test2(catList);
List<Animal<Cat>> animalList = new LinkedList<>();
test2(animalList);
Java version:
java 9.0.4
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)

Cannot return supertype in generic method

Some example code:
public class Main {
class SomeType {
}
class A {
protected <T extends SomeType> T createSomething()
{
return null; // Don't worry about this.
}
}
class B extends A {
#Override
protected <T extends SomeType> T createSomething()
{
SomeType orig = super.createSomething();
// do some stuff with orig
return orig;
}
}
}
What am I getting wrong here?
On the line
return orig;
the compiler spits out the error that
Type mismatch: cannot convert from Main.SomeType to T
Thanks to <T extends SomeType>, we can be sure that T is always a subtype of SomeType, right? So why can't I just return the supertype SomeType?
I've already read <? extends SuperType> cannot be applied to SuperType and Explanation of the get-put principle but I don't see how it applies here.
Thanks to <T extends SomeType>, we can be sure that T is always a subtype of SomeType, right?
Right.
So why can't I just return the supertype SomeType?
Because it might not be a T!
Put it this way - imagine SomeType is Object, and T is String. You're suggesting that this code should work:
String foo() {
return new Object();
}
It works the other way round - you can return a subtype reference for a method declared to return a supertype, but that's different.
The fix is easy though - just change orig to be of type T:
T orig = super.createSomething();
Then it compiles with no problems.

Categories