首页 > 解决方案 > 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).

标签: scalagenericsstructural-typingscala-generics

解决方案


尝试

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

java.util.HashSet#add(实际上java.util.AbstractCollection#add)的签名并且java.util.LinkedList#add

boolean add(E e)

结构类型的反射调用在运行时解决。在运行时,由于类型擦除,泛型E只是Any(又名Object)。

更多细节:具有泛型类型的结构类型


推荐阅读