Generics Questions / BUG ?

I playing around with generics and have the following four Java Objects (2 interfaces + 1 abstract class + 1 enum), and an obscure error message. I hope anyone here can explain the reason or maybe even agree with me that it could be a bug:


interface Scalar<T> {
    public abstract T    getValue();    
    public abstract void setValue(T value) throws UnsupportedOperationException, NullPointerException;    
}

interface Vector<T> {
    T    getElement(int index) throws IndexOutOfBoundsException;
    void setElement(int index, T value) throws UnsupportedOperationException, IndexOutOfBoundsException, NullPointerException;    
}


abstract class Vector2<T> implements Vector<T> {
    
    public static enum Elements {
        X {
            <T> Scalar<T> get(final Vector2<T> source) {
                return new Scalar<T>() {
                    public T    getValue()        { return source.getElement(0);        }
                    public void setValue(T value) {        source.setElement(0, value); }
                };
            }
        },
        Y {
            <T> Scalar<T> get(final Vector2<T> source) {
                return new Scalar<T>() {
                    public T    getValue()        { return source.getElement(1);        }
                    public void setValue(T value) {        source.setElement(1, value); }
                };
            }            
        };
        
        
        abstract <T> Scalar<T> get(final Vector2<T> source);
    }

}

Now, l added a single method to the Vector2 class:


// Compiler Error:
//incompatible types
//found   : Scalar<T>
//required: Scalar<T>
// lines
//this.x = x.get(Vector2.this);  
//this.y = y.get(Vector2.this);    
public Vector2<T> get(Elements x, Elements y) {

    final class Swizzle<T> extends Vector2<T> {

        private final Scalar<T> x, y;

        Swizzle(final Vector2.Elements x, final Vector2.Elements y) {
            this.x = x.get(Vector2.this);
            this.y = y.get(Vector2.this);
        }

        public T    getElement(int index)      { return this.x.getValue();  }
        public void setElement(int index, T x) {        this.x.setValue(x); }

    }  

    return new Swizzle<T>(x, y);
} 

Note, that I am somehow scared by the error message part: found: Scalar, required: Scalar

Then, I thought a local class is like a non-static inner class and therefore implicitly derives the generic type T, but the following also doesn’t work too:


// Compiler Warning/Error with Xlint:unchecked:
// warning: [unchecked] unchecked conversion
// found   : Swizzle
// required: Vector2<T>
// lines:
// return new Swizzle(x, y);  
public Vector2<T> get(Elements x, Elements y) {

    final class Swizzle extends Vector2<T> {

        private final Scalar<T> x, y;

        Swizzle(final Vector2.Elements x, final Vector2.Elements y) {
            this.x = x.get(Vector2.this);
            this.y = y.get(Vector2.this);
        }

        public T    getElement(int index)      { return this.x.getValue();  }
        public void setElement(int index, T x) {        this.x.setValue(x); }

    }  

    return new Swizzle(x, y);
}

Again, I was confused that required: Vector but found is Swizzle. Note that Swizzle derives from Vector2 and therefore should be compatible!

Finally, I used an inner class and it worked fine, but I’d preferred a local class:


// Works but I'd prefer a local class instead of an inner class:    
final class Swizzle2 extends Vector2<T> {

    private final Scalar<T> x, y;

    Swizzle2(final Vector2.Elements x, final Vector2.Elements y) {
        this.x = x.get(Vector2.this);
        this.y = y.get(Vector2.this);
    }

    public T    getElement(int index)      { return this.x.getValue();  }
    public void setElement(int index, T x) {        this.x.setValue(x); }
}                   

public Vector2<T> get(Elements x, Elements y) {
    return new Swizzle2(x, y);   

} 

Thanks for comments,
Michael

Btw. I know that the code make no sense ::slight_smile:

Your T is not T :wink:
Remove the leading from your get methods in the Elements enum.

And try the generics quiz: http://www.grayman.de/quiz/java-generics.quiz

Removing the leading from the enum methods would make the class useless since it cannot return Scalar. Note that enums don’t derive the because they are implictly static inner classes.

Further IMO, the T is the right T.

T foo(T t); returns exactly the passed T, because its a generic method, which has nothing todo with the class.
=> the T of the returned scalar is the same T as in the Vector2 class (Vector2.this) taken as argument:
this.x = x.get(Vector2.this);

Btw the generics quiz doesn’t contain an example about local or inner classes, so it doesn’t fit here.

since the aboe code was uneccesary complex for showing the problem or potential compiler bug, here is a simpler version:


abstract class Foo<T> {
    
    enum Bar {
        A,B;
        
        <T> T get(T t) { return t; }        
    }
    
    abstract T get();
    
   Foo<T> error(Bar bar) {
        
        class Error<T> extends Foo<T> {
            
            final T t;
            
            Error(final Bar bar) {
                // error: incompatible types, but Bar.get is a generice method, 
                // which returns the same as the specified as argument.
                this.t = bar.get(Foo.this.get()); 
            }
            
            T get() { return t; }            
        }
                
        return new Error<T>(bar); // 
    }
    
    
   Foo<T> error2(Bar bar) {
        
        class Error2 extends Foo<T> {
            
            final T t;
            
            Error2(final Bar bar) {
                this.t = bar.get(Foo.this.get());
            }
            
            T get() { return t; }            
        }
                
        // works so the above def. seems legal, so the T must be the T of Foo.this
        //return null;
        
        // warning: unchecked conversion, but why? Error2 extends Foo<T>
        return new Error2(bar); 
    }
    
    
    class Works extends Foo<T> {

        final T t;

        Works(final Bar bar) {
            this.t = bar.get(Foo.this.get());
        }

        T get() { return t; }
    }    
    
   Foo<T> works(Bar bar) {
        // works, but the above code should work too!!
        // and I wanted to use a local not an innver class.
        return new Works(bar); 
    }   
}

Sorry, was a bit quick to answer, I did’nt try it first. I meant the T is not the right T from the compiler perspective regarding the methods signature. However, if you change Swizzle extends Vector2 to Swizzle extends Vector2 it works, since you introduced a second type parameter T by Swizzle

Yes I know, but it’s fun and interesting. :wink:

Same here, in class Error extends Foo you have two Ts: a new type parameter (in class Error) and the original T (in Foo)

Isn’t your solution equal to my second examples, which lead to the compiler warning: unchecked conversion, although Swizzle/Error2 derives from Vector2/Foo ?

Damn, German is required for Java generics? No wonder they are so confusing :slight_smile:

Actually yes and it does not give me compiler warnings… hmm. I am using 1.5_05 on windows

Oh great, that’s it!

I used Java SE 6.0 Release 1 Developer Preview 6, which is based on SUN’s JDK 1.6.0_b88.
I switched to Java 5 and the warning was gone :slight_smile:
(Both on MacOS X)

Guess I have to fill a bug report for Java 6…