Problems with generics, super, and extends - java

I have a (generic) class that holds meta data for other classes. The meta data is used in several ways (writing and reading XML data, database, output as text, etc). So far this works. But I have come across a problem when using all of this for classes that inherited from other classes.
Please have a look at the following code (I have tried to produce a minimal example that is compilabe except the line marked below):
class A {
public Meta<? extends A> getMeta() {
return new Meta<A>();
}
public void output() {
/*
* Error shown in eclipse for the next line:
* The method output(capture#1-of ? extends A) in the type
* Outputter<capture#1-of ? extends A> is not applicable for the arguments
* (A)
*/
getMeta().getOutputter().output(this);
}
}
class B extends A {
#Override
public Meta<? extends B> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}
I can change A.getMeta() to return Meta<A> to make above line compilabe, but then I cannot override it as Meta<B> getMeta() in class B.
Any ideas on how to solve this?

What if you do this? It requires one more class, but it seems it is going to work:
class T{
//put common methods here, generic methods are not common, so they will not be here
}
class A extends T{
public Meta<A> getMeta() {
return new Meta<A>();
}
public void output() {
/*
* Error shown in eclipse for the next line:
* The method output(capture#1-of ? extends A) in the type
* Outputter<capture#1-of ? extends A> is not applicable for the arguments
* (A)
*/
getMeta().getOutputter().output(this);
}
}
class B extends T {
public Meta<B> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}
if you do not want to create another method you can use composite. There are many good discussions about compositions over inheritance.
Everything will be the same except A and B classes:
class A{
public Meta<A> getMeta() {
return new Meta<A>();
}
...
}
class B {
private class A a;
public Meta<B> getMeta() {
return new Meta<B>();
}
//use a here if you need, a is composed into B
}

The reason you cannot override public Meta<A> getMeta() with public Meta<B> getMeta() is that instances of B will be castable to A, and such a casted instance would need to return a Meta<A>. While it may be that a Meta<B> can serve as a Meta<A>, the compiler doesn't know that.
Imagine instead that you are returning List<A> and a List<B>. It is allowable to put instances of A and B into a List<B>, but it is not allowable to put instances of B into a List<A>, so the List<B> that is actually being returned by B can not serve as a List<A>.
Changing List<A> to List<? extends A> allows the code to compile, because List<B> is technically a subclass of List<? extends A>, but it will not allow you to do everything you may expect.
B b = new B();
A casted = (A)b;
casted.getList().add(new A());
The compiler will accept the first and second line without issue, but it will take issue with the third:
The method add(capture#1-of ? extends A) in the type List<capture#1-of ? extends A> is not applicable for the arguments (A)
If you investigate a bit, you'll find that this casted variable will accept neither elements of A nor B. The compiler has remembered that the object was casted and may not actually be able to accept anything that extends A.
I'm trying to hunt down documentation for this behavior, but I'm failing. Eclipse tooltips are suggesting that I should give it an element of type null, which is obviously nonsense. I'll update if I find anything on it.
EDIT: The behavior described is a product of "Capture Conversion" as described here. Capture Conversion allows wildcards to be more useful by changing the bounds of type arguments over the course of assignments and casts. What happens in our code is simply that the bounds are constricted to the null type.

I will answer this myself since I found a working solution.
Although this solution is not type-safe, it works and requires the least changes to my existing codebase. If anyone comes up with something that works and doesn't require the #SuppressWarnings, I will accept that answer.
class A {
Meta<?> getMeta() {
return new Meta<A>();
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public void output() {
Outputter out = getMeta().getOutputter();
out.output(this);
}
}
class B extends A {
#Override
public Meta<?> getMeta() {
return new Meta<B>();
}
}
class Meta<CLS> {
public Outputter<CLS> getOutputter() {
return null;
}
}
class Outputter<CLS> {
public void output(CLS obj) {
}
}

Related

Is it possible to insert this into a generic return parameter? [duplicate]

I have an interface with a type parameter that allows its conversion into the same type with another type parameter. Like this:
interface Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f);
}
I now want to impose a stricter requirement on the return type: I want the convert method to only return the same type as it was called on. Like this:
class GoodInterfaze<A> implements Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f) {
// return new GoodInterfaze<B>(); // I want this to be allowed by compiler
// return new BadInterfaze<B>(); // I want this to be a compilation error
return null;
}
}
class BadInterfaze<A> implements Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f) {
// return new GoodInterfaze<B>(); // I want this to be a compilation error
// return new BadInterfaze<B>(); // I want this to be allowed by compiler
return null;
}
}
The Interfaze interface is under my control, so I can add extra type parameters to it (or its methods) when needed. Do Java generics allow for anything like this?
You can get close by doing this.
public interface Interfaze<T extends Interfaze<T>> {
T convert();
}
Then you can do
public class Main {
public static class Good implements Interfaze<Good> {
#Override
public Good convert() { return new Good(); } // Compiles
}
public static class Bad implements Interfaze<Bad> {
#Override
public Bad convert() { return new Good(); } // Doesn't compile
}
}
This idea of using recursive bounds like this is very common. I personally dislike it as it's very confusing and because it doesn't mix well with inheritance. For example, you can't make a subclass SubGood of Good that implements Interfaze<SubGood> because you can't implement the same generic interface with 2 different type arguments. It only really works if all implementing classes cannot be extended (that's why Enum<E extends Enum<E>> is ok).

Why does the generic type of a superclass field not get erased to the concrete bound in a subtype?

package ir.openuniverse;
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
System.out.println(A.class.getField("t").getType().getName());
}
}
class A extends B<D> {}
class B<T extends C> {
public T t;
}
class C {}
class D extends C {}
The output is ir.openuniverse.C. Why? I expect D!
EDIT:
This question wasn't about workarounds or alternative ways. So answers aren't about workarounds. For alternative ways see myself answer below.
This happens because of type erasure.
Java compiles your generic class B<T> into byte code suitable for use with all classes that may reference it, including any class that may be extending B<T>.
Since T is restricted to classes extending C, Java knows that any value that you could assign B.t will extend C, so it compiles your class into an equivalent
class B {
C t;
}
At this point any assignment of t would work; reading from t would yield C, though, so the compiler must do some "magic" to fix this. Specifically, the compiler inserts type casts in places where the subtype is known. It may also generate bridge methods if necessary. See the link at the top of the answer for the details.
During compilation, Java's type erasure change
class B<T extends C> {
public T t;
}
to :
class B<C> {
public C t;
}
Since getType() identifies the declared type for the field, the output is ir.openuniverse.C
Thanks for all's helps. Finally, I forced to change my A's definition to:
class A extends B<D> {
public D t;
}
It's sufficient for my purpose (although I don't like it at all!).
EDIT:
Above way is not a fundamental way. See #DanielPryden's first two comments below.
Alternative workaround:
class A extends B<D> {
#Override public D getT() { return super.getT(); }
}
class B<T extends C> {
private T t;
public T getT() { return t; }
}
And in main():
System.out.println(A.class.getMethod("getT").getReturnType().getName());
Output: ir.openuniverse.D
Another way:
NOTE: This is not a solution to the main problem (See #DanielPryden's fourth comment below). But maybe helps you (like me).
This workaround can be used when you have at least one instance of A:
public class Main {
public static void main(String[] args) {
A a = new A();
System.out.println(a.t.getClass().getName());
// Or via reflection:
// System.out.println(a.getClass().getField("t").get(a).getClass().getName());
}
}
class A extends B<D> {
{ t = new D(); }
}
class B<T extends C> {
public T t;
}
Output: ir.openuniverse.D

Polymorphic method return type down-casting in java

So I don't know if this is possible I've tried searching it but maybe my search terms are off. Basically I'm wondering, is there a way to create a generic function/method in a super class that returns the downcast object.
class A {
public <downcasted type (in this example B if called from a B instance)> test() {
return this;
}
}
class B extends A { }
B b = new B().test()
basically having "test()" return the B instance as type B even know the function/method is declared purely in the parent class?
I know I can cast the variable, tho having many functions some of which may return Lists of the class type, etc become troublesome. I also realize I could #override the function in B and do a "return (B)this.super()" thing, but again wrapping many functions is tedious and makes makes updating the base classes code more painful.
I also know you can do
"class A<T extends A>"
and then define B as
"class B extends A<B>"
but then if you want to make a "C" that extends "B" it breaks.
So is this type of behavior possible? If so, what is it called and how do I implement it?
An example as to where this behavior could be useful would be any base data structures you want to make extendable like an N-Ary Tree that you extend into oct/quad tree structure and/or an extended class that adds a "Name" and "Attributes" or something for a xml-like node.
Edit:
This seems to work(as far as the linter is concerned), it's a bit more work to implement the base methods but it's got the desired end result as far as I can tell. That said when I attempt to run it, it gives me a "cannot find symbol: class type" error. :S
static class D extends auto {
final Class type = getClass();
#SuppressWarnings("unchecked")
public <T extends type> T test() {
return (T)type.cast(this);
}
}
static class E extends D { }
static class F extends E { }
static {
D d = new D().test();
E e = new E().test();
F f = new F().test();
}
Update
There is a simpler way, which seems to work:
class Alpha {
#SuppressWarnings("unchecked")
<T extends Alpha> T test() {
return (T) this;
}
}
class B extends A { }
However, that does not support method chaining.
Original post
You need test() to return a subtype of A, rather than A itself. In order to do this, the signature of the A class could be this:
class A<T extends A<?>> {
#SuppressWarnings("unchecked")
public T test() {
return (T) this;
}
}
If you create a class B extending A, you will need B.test() to return an instance of B, without needing to override test() returning a specific type. You could then do something like this:
class B<T extends B<?>> extends A<T> { }
Now T is a subclass of B, and because test()'s return type is T, it will return a B instance. Further subclassing can be done in the same way:
class C<T extends C<?>> extends B<T> { }
And statements like this will work:
C<?> c = new C<>().test();

Java class information is lost when extend class with more generics

This seems to be a compiler issue, or maybe this is there by design.
ClassA is a class with two generics. ClassB will extend ClassA by providing one solid generic type, but still expose another one.
In the following example, E will be passed in type that will extend ClassA, so when any method is called, then returned type will still be the subtype which enables to call the subtype method if needed. The motivation behinds this is to do a builder pattern, e.g.
ClassB b = new ClassB<String>().m1().m2().m3().m4()......
public class ClassA<E, T> {
public final E e;
public final T t;
public ClassA(T t) {
this.e = (E)this;
this.t = t;
}
public E printA() {
System.out.println("AAAAAA");
return e;
}
}
public class ClassB<T> extends ClassA<ClassB, T> {
public ClassB(T t) {
super(t);
}
public ClassB printB() {
System.out.println("BBBBBB");
return this;
}
public static void main(String[] args) {
ClassB<String> classB = new ClassB<>("Hello World");
// classB.printA().printA().printA(); // This will fail, after the second printA() return Object type instance instead of ClassB.
System.out.println(classB.printA().printA().getClass()); // This will print "class ClassB", so the class information it still there.
((ClassB)classB.printA().printA()).printA(); // Casting the instance to ClassB again will make it work again.
}
}
The problem is that when you call the method two times, the return instance type will be lost, so it will be an Object type, and cannot call any ClassA/B method without casting it. This is super weird.
Any idea?
Your class ClassB is a generic one, but you are opting out of generics when not providing a type parameter.
And you are doing exactly that here
public class ClassB<T> extends ClassA<ClassB, T>
^^^^^^
and here
public ClassB printB()
^^^^^^
So simply change these lines to
public class ClassB<T> extends ClassA<ClassB<T>, T>
^^^
and
public ClassB<T> printB()
^^^
Then it will work.

Bounded types: Multiple bounds

I have read this article here and tried to figure out how to work with bound types. What I try to achieve is a parametrized method that handles four different cases:
T extends B only
T extends B and I (here D)
T extends I only
everything else
So here is the code:
public class Main {
public static void main(String... args) {
B b = new B();
D d = new D();
I i = new I() {
};
handle("aaasd");
handle(b);
handle(d); <---- Problem 1
handle(i);
}
public static class B {
}
public static interface I {
}
public static class D extends B implements I {
}
public static <T> void handle(T objT) {
System.out.println("T");
}
private static <T extends B> void handle(T obj) {
System.out.println("B");
}
public static <T extends B & I> void handle(T objT) { <--- Problem 2
System.out.println("B+I");
}
private static <T extends I> void handle(T obj) {
System.out.println("I");
}
}
The compiler complains and says two things:
Ambiguous call
The method handle(Main.D) is ambiguous for the type Main
I guess the problem is caused by the same cause as Problem number 2. The & I clearly bounds the type of T to a subtype of B AND I thus removing ambiguity in my opinion.
Same erasure handle
Method handle(T) has the same erasure handle(Main.B) as another method in type Main
My guess is that this is the real cause for all the problems. Java somehow removes bounding to I during runtime? But when I call the method with type B this doesn't call the annoted method.
Can someone explain how I fix the problem/distinguish between B, B&I and I?
Java somehow removes bounding to I during runtime?
No, Java removes every type information at runtime (except for reflection purposes) which is called type erasure.
Using bounds the compiler would be able to translate your code to handle(Object), handle(B) and handle(I) but in the T extends B & I case the compiler would get conflicts.
AFAIK, there's no way to fix this without having a common bound, e.g. T extends D instead of T extends B & I where D extends B implements I or to change the method name or add another parameter.
Another way might be to add the logic in the B+I case to either the B or I method and check for the second condition inside, e.g.
private static <T extends B> void handle(T obj) {
if( obj instanceof I) {
System.out.println("B+I");
}
else {
System.out.println("B");
}
}
There's a concept known as type erasure that applies to all generics in Java. With generic methods, after compilation, the methods in the byte code appear as their erasure, so
public static <T> void handle(T objT) {
System.out.println("T");
}
private static <T extends B> void handle(T obj) {
System.out.println("B");
}
public static <T extends B & I> void handle(T objT) { <--- Problem 2
System.out.println("B+I");
}
private static <T extends I> void handle(T obj) {
System.out.println("I");
}
actually become
public static void handle(Object objT) {
System.out.println("T");
}
private static void handle(B obj) {
System.out.println("B");
}
public static void handle(B objT) {
System.out.println("B+I");
}
private static void handle(I obj) {
System.out.println("I");
}
The left-most bound of a type variable is what a parameter of that type gets replaced with. As you can see, both your 2nd and 3rd method have the same name and same parameter types, ie. the same signature. This cannot be allowed by the compiler.
However, the syntax of bounds forces you to provide the class type before any interface types so
<T extends I & B>
wouldn't work. It also wouldn't work because your 4th method would again have the same erasure.
Additionally, invoking
handle(d);
is a problem since both the 2nd and 4th method could handle it, none is more specific. This is known as overloading ambiguity.

Categories