2

Java

Set<Long> set = new HashSet<Long>();
set.add(100);
long x = 2;
foo(x, set);

Scala

def foo(a: Long, b: java.util.Set[Long])

Error:

could not parse error message:        
  required: long,Set<Object>    
  found: long,Set<Long>     
  reason: actual argument Set<Long> cannot be converted 
      to Set<Object> by method  invocation conversion

Then, I modified my Java code to resolve the compile-time error.

Set<Object> set = new HashSet<Object>();

However, the resolution of the compile-time error came at the expense of type safety. How can I properly resolve the above error?

EDIT

After @som-snytt resolved my issues here, I ran into this problem. I don't think it's the same question since , in my linked question, using, in Scala, foo(Long, java.util.Set[Long]) worked when calling (from Java) ScalaObject.foo(long, Set[Long])

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • 1
    This doesn't really answer your question but changing foo's signature to `def foo(a: Long, b: java.util.Set[java.lang.Long])` should get rid of the compilation error. – mantithetical Jan 31 '14 at 15:59
  • `foo(x: Long, set: java.util.Set[java.lang.Long] = { val y: scala.collection.immutable.Set[Long] = set.asScala.toSet` is giving me: `[INFO] found : scala.collection.immutable.Set[Any] [INFO] required: scala.collection.immutable.Set[Long]` – Kevin Meredith Jan 31 '14 at 16:15
  • That makes sense. I couldn't have predicted that without having seen foo's body. So you could either change all the `Long`s to `java.lang.Long` or import `JavaConverters` in foo's body as noted in the answer below. – mantithetical Jan 31 '14 at 16:21
  • 1
    [I appreciate your help - thank you. I didn't mean to post the response as criticism] – Kevin Meredith Jan 31 '14 at 16:22

2 Answers2

5

The types are wrong. The type of set in the Java code is java.util.Set[java.lang.Long], while the type in Scala is java.util.Set[scala.Long]. The scala.Long type is treated as the primitive long in Java, when not erased, and as java.lang.Object when erased (as you uncovered).

So either change Scala's type to Java's type to match. It's unfortunate that scala.Long erases to java.lang.Object, but necessary.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • is one approach better? My gut tells me that modifying my Java code to use `Set` is a poor choice compared to changing my Scala code's signature to `foo(x: Long, set: java.util.Set[java.lang.Long]`, but I wasn't sure how to resolve the bug mentioned in my comment to manithetical. In REPL it works: `scala> val x: java.util.Set[java.lang.Long] = new java.util.HashSet[java.lang.Long]() x: java.util.Set[Long] = [] || scala> x.add(100) res12: Boolean = true || scala> x.asScala.toSet res17: scala.collection.immutable.Set[Long] = Set(100)` – Kevin Meredith Jan 31 '14 at 16:29
  • [side-question, `erase` means how Java reads a type at run-time?] I've read your helpful post on `type erasure` (http://stackoverflow.com/questions/1094173/how-do-i-get-around-type-erasure-on-scala-or-why-cant-i-get-the-type-paramete) a few times, but don't fully understand it yet. – Kevin Meredith Jan 31 '14 at 16:32
  • @KevinMeredith I prefer changing the type on the Scala method, though it could be inconvenient if you plan to do math on those longs. If they are identifiers of some kind, you should really look into creating a class for them -- it's the same cost as `Long`, and provides type safety. – Daniel C. Sobral Jan 31 '14 at 16:34
  • @KevinMeredith As for "erasure", before Java 1.5, each type in the Java language had a direct correspondence in bytecode (which is what the JVM reads). When Java 1.5 introduced generics, instead of adding generics to bytecode they resorted to "erasing" the generics information from the types in the bytecode, and leaving the JVM unchanged. So, yes, you can think of it as how Java reads a type at run-time. Importantly, Java the Language You Write and Java the Language that Gets Executed (bytecode) are different languages, though both are called Java. – Daniel C. Sobral Jan 31 '14 at 16:37
0

You probably want to import JavaConverters in this case.

import scala.collection.JavaConverters._
def foo(a: Long, b: java.util.Set[Long]) = ???
mantithetical
  • 1,755
  • 2
  • 16
  • 21