2

Assuming I have the following classes in an application:

A base class:

public abstract class Animal {}

A subclass

public class Dog extends Animal {

public void bark() {
    System.out.println("woof");
}
}

A class from a third party library:

public class ThirdPartyLibrary /* *cough* Hibernate *cough* */{
public List find() {
    return new ArrayList() {
        // fetched from the db in the actual app of course
        {
            add(new Dog());
            add(new Dog());
            add(new Dog());
        }
    };
}
}

And a utility class:

public class Util {

public <E extends Animal> E findUnique(List<E> animals) {
    return animals.isEmpty() ? null : animals.get(0);
}

/**
 * @param args
 */
public static void main(String[] args) {
    Dog d = new Util().findUnique(new ThirdPartyLibrary().find());
    d.bark();
}   
}

The eclipse compiler issues the following warnings:

  • Type safety: The expression of type List needs unchecked conversion to conform to List
  • Type safety: Unchecked invocation findUnique(List) of the generic method findUnique(List) of type Util

But the build fails when compiling with Sun's javac with the error:

 incompatible types
    [javac] found   : Animal
    [javac] required: Dog
    [javac] Dog d = new Util().findUnique(new ThirdPartyLibrary().find());
    [javac]                          ^

Is there something else I can do to make this code compile in javac, apart from an explicit cast to (List<Dog>) before the call to findUnique?

Jeshurun
  • 22,940
  • 6
  • 79
  • 92
  • For the `javac` problem, are you compiling using the same version of the JDK as your Eclipse install? For the warning, have you checked under the Java preferences in Eclipse, under errors and warnings, to see if it's a warning you can turn off? – jefflunt Oct 12 '11 at 21:53
  • Yes, both use version 1.6. And I am really not worried about the warnings, as I have @SuppressWarnings("unchecked") before the method anyway. The code when compiled in eclipse and run with sun java works fine as well. – Jeshurun Oct 12 '11 at 21:59
  • Did you try: `Dog d = (Dog)new Util().findUnique(new ThirdPartyLibrary().find());` Obviously, the return types are incompatible, you could be returning something other than a Dog. – blackcompe Oct 12 '11 at 22:13
  • @blackcompe Yes that compiles as well. – Jeshurun Oct 12 '11 at 22:31

1 Answers1

5

No, there's nothing else you can do, and you must have something fishy going on in Eclipse, because Eclipse wouldn't be able to compile that (as Java code), either. Since the list you pass to findUnique() is a raw type, the compiler can't determine what the E should be in public <E extends Animal> E findUnique(List<E> animals). Therefore E essentially becomes ?, and what you're trying to do is basically this:

List<? extends Animal> l = new ArrayList<Animal>();
Dog dog = l.get(0);

It should be obvious that that can never work. The only way to get close to making this work is to add a method argument that makes E have a distinct value. Something like this would work:

public <E extends Animal> E findUnique(List animals, Class<E> type) {
    return animals.isEmpty() ? null : (E) animals.get(0);
}
Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • Thanks for the explanation, I see what is going on now. Perhaps this is an eclipse bug that should be reported? I'm using Indigo Service Release 1 on Ubuntu with jdt version 3.6.1.v20110803_r371 – Jeshurun Oct 13 '11 at 05:02
  • Apparently this is a known bug in Eclipse. See the bug reports [here](https://bugs.eclipse.org/bugs/show_bug.cgi?id=333011) and [here](https://bugs.eclipse.org/bugs/show_bug.cgi?id=340506). Also there is another question related to this issue [here](http://stackoverflow.com/questions/5633424/is-it-a-eclipse-or-maven-compiler-plugin-bug-the-generics-class-cast-issue) and another one with method overloading [here](http://stackoverflow.com/questions/5361513/reference-is-ambiguous-with-generics). – Jeshurun Jan 12 '12 at 15:08