scalagenericsstructural-typingscala-generics

Structural types with generics in Scala


I tried to define a structural type which is matched to an instance with a generic type. Like in this example code:

class ExampleTest extends FlatSpec with Matchers {
  def add[T](container: {def add(s: T): Boolean})(element: T): Unit = {
    container.add(element)
  }

  val set = new java.util.HashSet[String]()
  add(set)("3")
  set.contains("3") shouldEqual true

  val list = new java.util.LinkedList[String]()
  add(list)("3")
  list.contains("3") shouldEqual true
}

but I get a compilation error:

Parameter type in structural refinement may not refer to an abstract type defined outside that refinement def add[T](container: {def add(s: T): Boolean})(element: T): Unit = {

When I get rid of the generic type in the method and write the code like this:

class ExampleTest extends FlatSpec with Matchers {
  def add(container: {def add(s: String): Boolean}): Unit = {
    container.add("3")
  }

  val set = new java.util.HashSet[String]()
  add(set)
  set.contains("3") shouldEqual true

  val list = new java.util.LinkedList[String]()
  add(list)
  list.contains("3") shouldEqual true
}

it compiles but I get a runtime exception:

java.lang.NoSuchMethodException: java.util.HashSet.add(java.lang.String)

My question is: How to correctly define the structural type, so it can work with Java collections?

Note that it cannot replace them with Scala collection (it will be used with a Java library).


Solution

  • Try

    import scala.language.reflectiveCalls
    
    def add[T](container: {def add(s: Any): Boolean})(element: T): Unit = {
      container.add(element)
    }
    
    val set = new java.util.HashSet[String]()
    add(set.asInstanceOf[{def add(s: Any): Boolean}])("3")
    set.contains("3") shouldEqual true
    
    val list = new java.util.LinkedList[String]()
    add(list.asInstanceOf[{def add(s: Any): Boolean}])("3")
    list.contains("3") shouldEqual true
    

    Signature for java.util.HashSet#add (actually java.util.AbstractCollection#add) and java.util.LinkedList#add is

    boolean add(E e)
    

    Reflective calls for structural types are resolved at runtime. At runtime because of type erasure generic E is just Any (aka Object).

    More details: Structural types with generic type