15

I had to discover I have Java code in my project, which compiles and runs fine in Eclipse, but throws a compilation error in javac.

A self-contained snippet:

import java.util.HashSet;
import java.util.Set;

public class Main {

    public static void main(String[] args) {
    Set<Integer> setOfInts = new HashSet<Integer>();
    Set<Object> setOfObjects = covariantSet(setOfInts);
    }

    public static <S, T extends S> Set<S> covariantSet(Set<T> set) {
    return new HashSet<S>(set);
    }

}

Compilation in javac returns:

Main.java:10: incompatible types
found   : java.util.Set<java.lang.Integer>
required: java.util.Set<java.lang.Object>
    Set<Object> setOfObjects = covariantSet(setOfInts);
                                           ^

This error now prevents building the project in Maven. As the Eclipse compiler is built to be more tolerant, I now have to assume the definition and usage of snippets as above static method is no valid Java?

7
  • Could it be that they are using different versions of the JDK (say Java 6 vs Java 7 for example) and this would be a bug in older JDK versions? Commented Nov 23, 2012 at 18:03
  • I use javac 1.6.0_37, and Eclipse Helios with compiler compliance level 1.6. Commented Nov 23, 2012 at 18:07
  • FYI I just tested your code and it compiles with Java 7 but not with Java 6. Commented Nov 23, 2012 at 18:09
  • @assylias Thanks, this is a helpful information. Wonder why that is exactly... Commented Nov 23, 2012 at 18:26
  • Are you aware that the T is unnecessary for covariantSet as it is here? (covariantSet(<Set<? extends S> set) is equivalent -- unless of course your actual method is more complex and uses T somehow, although I at the moment can think of no good example where the difference of S and T would be relevant.) Commented Nov 24, 2012 at 0:25

5 Answers 5

12

It seems that Sun's 1.6 JDK can't infer the correct type. The following seems to work on my machine:

Set<Object> setOfObjects = Main.<Object, Integer>covariantSet(setOfInts);

Note that you must invoke the static method prefixed with the class name

Sign up to request clarification or add additional context in comments.

1 Comment

Accepting this answer, since it provides the least intrusive workaround to solve the stated problem.
10

You are right. This problem indeed exists. Eclipse does not use javac. It uses its own compiler.

Actually javac is "right". Generics are erasures. Type S is not included into your byte code, so jvm does not have enough information about the return type at runtime. To solve the problem change the method prototype as following:

public static <S, T extends S> Set<S> covariantSet(Set<T> set, Class<S> returnType)

Now the return type is passed to the method at runtime and compiler should not complain.

4 Comments

It seems to compile ok with javac / JDK 7
Your solution works as described. I was suspecting type erasure to be cause. Interestingly though, as assylias mentioned above, it seems to compile in java 7.
@mtsz that would mean that java 7 improved on the generic inference
I think it is a bug in JDK 6. as AlexR mentioned generics are erasures. But that also means that the Set does not need the information of beeing of type <S> at runtime. Hence the quasi equivalent after comilation is simply Set setOfObjects = Main.covariantSet(setOfInts); No need of generics at all. And for compile time the generic types should be injected correctly just like the eclipse compiler does.
0

In your Maven build skript you have set the compiler version.

In Ant it lookes like this:

<property name="source.version" value="1.5" />

search for 1.3 or 1.4, or compile to find that value in the maven skripts

With value 1.5 the compiler will accept the generics (see your error messages)

2 Comments

It appears to be recognizing generics just fine, though.
Unfortunately, setting the java source and target-version of maven-compiler-plugin to 1.5 does not help.
0

I know it's old question, but I want to mention, the function could be written as:

import java.util.HashSet;
import java.util.Set;

public class Main {

public static void main(String[] args) {
        Set<Integer> setOfInts = new HashSet<Integer>();
        Set<Object> setOfObjects = covariantSet(setOfInts);
    }

    public static <S> Set<S> covariantSet(Set<? extends S> set) {
        return new HashSet<S>(set);
    }

}

It's a little bit cleaner and you can use the function exactly how you intented to(with implicit generic typing).

Comments

-1

Add the next plugin to your pom.xml:

<plugin>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>2.3.2</version>
     <configuration>
          <source>1.6</source>
          <target>1.6</target>
     </configuration>
</plugin>

1 Comment

I've tried your snippet which denotes an older version of the maven-compiler-plugin. It does not help. I am using the most current version 2.5.1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.