20

This seems to compile fine with Java 7, and any version of the Scala libraries:

public static void main(String[] args) {
    scala.collection.immutable.Set<String> set = new scala.collection.immutable.HashSet<String>();
    Iterator<String> iterator = set.iterator();
}

It also compiles fine with Java 8 and Scala 2.11.5+. But with Java 8 and Scala 2.11.4, Eclipse complains:

The method iterator() is ambiguous for the type Set<String>

I don't understand this. You might get ambiguity over which overloaded method to select in some contexts, but surely not if you're not passing any arguments?

The really weird thing is that if I recast it like this:

public static void main(String[] args) {
    Iterator<String> iterator = new scala.collection.immutable.HashSet<String>().iterator();
}

then the complaint goes away. This seems to me to be exactly equivalent to the version above. So why would it now compile fine?

chiastic-security
  • 20,430
  • 4
  • 39
  • 67

2 Answers2

10

If we compare the javap output of scala.collection.immutable.Set, we get for the 2.11.4:

public interface scala.collection.immutable.Set<A> 
    extends 
        scala.collection.immutable.Iterable<A>, 
        scala.collection.Set<A>, 
        scala.collection.generic.GenericSetTemplate<A, 
        scala.collection.immutable.Set>, 
        scala.collection.SetLike<A, scala.collection.immutable.Set<A>>, 
        scala.collection.Parallelizable<A, 
        scala.collection.parallel.immutable.ParSet<A>> {
    public abstract scala.collection.generic.GenericCompanion<scala.collection.immutable.Set> companion();
    public abstract <B> scala.collection.immutable.Set<B> toSet();
    public abstract scala.collection.immutable.Set<A> seq();
    public abstract scala.collection.parallel.Combiner<A, scala.collection.parallel.immutable.ParSet<A>> parCombiner();
}

and for the 2.11.5:

public interface scala.collection.immutable.Set<A>
    extends 
        scala.collection.immutable.Iterable<A>, 
        scala.collection.Set<A> {
    public abstract scala.collection.generic.GenericCompanion<scala.collection.immutable.Set> companion();
    public abstract <B> scala.collection.immutable.Set<B> toSet();
    public abstract scala.collection.immutable.Set<A> seq();
    public abstract scala.collection.parallel.Combiner<A, scala.collection.parallel.immutable.ParSet<A>> parCombiner();
}

The version 2.11.4 is not correct, it is a violation of the Java Virtual Machine Specification, section 4.7.9.1:

A class signature encodes type information about a (possibly generic) class declaration. It describes any type parameters of the class, and lists its (possibly parameterized) direct superclass and direct superinterfaces, if any. A type parameter is described by its name, followed by any class bound and interface bounds.


This is clearly explained in this scalac issue, which have been fixed in the... 2.11.5

gontard
  • 28,720
  • 11
  • 94
  • 117
  • Good spot! I wonder why it doesn't surface in Java 7? Perhaps something to do with default implementations of interface methods in Java 8. That does give a potential source of ambiguity with which method should be invoked. – chiastic-security May 21 '15 at 14:31
  • 1
    I have not investigated why it works with java 7. javac compiles with all configurations (java[7,8] scala-libraries-2.11.[4,5]). The java 8 eclipse compiler is less permissive. – gontard May 21 '15 at 14:43
7

This may not be a strictly Java 8/Scala library issue. It may be related to Eclipse. Which version of Eclipse are you using? This sounds somewhat like this issue in Eclipse 4.4: Java 8 generics thinks single method is ambiguous

Peter
  • 6,354
  • 1
  • 31
  • 29
  • This might well be related. I'm pretty sure it isn't the whole story. Yesterday, with essentially the same thing but in a more complicated context, IntelliJ compiled it fine but Maven complained; today, trying to construct a minimal example, I've found that Maven compiles it fine but Eclipse complains. Weirder and weirder. – chiastic-security May 21 '15 at 11:35