2
votes

In Programming in Scala, the following example is given to show how to reference a Java class with wildcards. The method javaSet2ScalaSet takes a type T. Typically, you can always explicitly supply a parameterized type. But in this case where (new Wild).contents is existential, a normal type parameter is not acceptable.

The compiler is able to perform some magic and infer an appropriate parameter when javaSet2ScalaSet is called without a type parameter. Viewing what the compiler supplies with scalac –Xprint:typer shows that it assigns a value of ?0 for T. This can’t be provided manually.

Is this a special case that only works for an inferred type, or is there some way to explicitly supply the type?

  // This is a Java class with wildcards
  public class Wild {
    Collection<?> contents() {
      Collection<String> stuff = new Vector<String>();
      stuff.add("a");
      stuff.add("b");
      stuff.add("see");
      return stuff;
    }
  }

  import scala.collection.mutable.Set
  import java.util.Collection

  abstract class SetAndType {
    type Elem
    val set: Set[Elem]
  }

  def javaSet2ScalaSet[T](jset: Collection[T]): SetAndType = {
    val sset = Set.empty[T]  // now T can be named!

    val iter = jset.iterator
    while (iter.hasNext)
      sset += iter.next()

    return new SetAndType {
      type Elem = T
      val set = sset
    }
  }

val setAndType = javaSet2ScalaSet((new Wild).contents)
2
You can write javaSet2ScalaSet[T forSome {type T}] (which is the same as javaSet2ScalaSet[_]). Is that what you mean?Alexey Romanov
You can't write javaSet2ScalaSet[T forSome {type T}]. The compiler complains with unbound wildcard type. Same with javaSet2ScalaSet[_]).ToddLC
Sorry, I see what you mean now.Alexey Romanov

2 Answers

1
votes

It's just a syntactic limitation, not an inherent one. Here's how you can declare it:

def javaSet2ScalaSet[C <: Collection[T] forSome { type T }](jset: C): SetAndType = {
  val sset = Set.empty[T forSome { type T }]

  val iter = jset.iterator
  while (iter.hasNext)
    sset += iter.next()

  return new SetAndType {
    type Elem = T forSome { type T }
    val set = sset
  }
}
0
votes

(Q1) >> Is this a special case that only works for an inferred type. <<

The compiler does not infer the type of your Java collection to be of type Collection. It only knows that it is a collection with elements of some type.

(Q2) >> or is there some way to explicitly supply the type? <<

No. An existential type is not to be supplied. It is not a free variable to be bound (since it is bound already).

Look at it this way: suppose you were able to provide the type. You would then expect the compiler to check that the provided type matches the type of elements in your Java collection. But there is no way for the compiler to determine that. So in case they wouldn't match, you would anyhow only find out at runtime.

If you would like to tell the Scala-compiler that you know that the elements in the java collection are of type String (e.g. you want to call x.length) then you could cast the elements with asInstanceOf[String]. But that will not introduce type-safety. You will again only find out at run-time if it would be incorrect.