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?

mtsz
  • 2,725
  • 7
  • 28
  • 41
  • 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? – assylias Nov 23 '12 at 18:03
  • I use javac 1.6.0_37, and Eclipse Helios with compiler compliance level 1.6. – mtsz Nov 23 '12 at 18:07
  • FYI I just tested your code and it compiles with Java 7 but not with Java 6. – assylias Nov 23 '12 at 18:09
  • @assylias Thanks, this is a helpful information. Wonder why that is exactly... – mtsz Nov 23 '12 at 18:26
  • Are you aware that the `T` is unnecessary for `covariantSet` as it is here? (`covariantSet( 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.) – arne.b Nov 24 '12 at 00:25
  • @arne.b I hope i understand you correctly - you mean substituting the method with `public static Set covariantSet(Set extends S> set) { return new HashSet(set); }`? Unfortunately this does not compile in neither EJC nor javac 1.6.x. – mtsz Nov 24 '12 at 00:51
  • Yes, I just wanted to point out that `T` is redundant in this simple case. Also, Raffaele's answer still applies and you need to specify one explicit type parameter less if the method has only one. :) – arne.b Nov 24 '12 at 01:03

5 Answers5

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

Community
  • 1
  • 1
Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • Accepting this answer, since it provides the least intrusive workaround to solve the stated problem. – mtsz Nov 24 '12 at 19:08
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.

AlexR
  • 114,158
  • 16
  • 130
  • 208
  • 1
    It seems to compile ok with javac / JDK 7 – assylias Nov 23 '12 at 18:11
  • 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 Nov 23 '12 at 18:25
  • @mtsz that would mean that java 7 improved on the generic inference – ratchet freak Nov 23 '12 at 22:28
  • 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 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. – cornz Jan 22 '14 at 13:50
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)

AlexWien
  • 28,470
  • 6
  • 53
  • 83
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).

NiematojakTomasz
  • 2,433
  • 20
  • 23
-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
    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 – mtsz Nov 23 '12 at 18:18