Search code examples
javascalagenericsscala-java-interop

Calling Java Generic Typed Method from Scala gives a Type mismatch error : Scala


This is my very first question down here, so i'll try to make it clear as far as i can. Other error: type mismatch; questions here are not related to this error.

I have this odd problem with scala/java inter-operability :

Let's suppose we have a Java class

public class JavaClass{
  public static <T> T[] toArray(Class<T> t, Collection<T> coll) {
    return null; // return null to make it simple
  }
}

And then i have another Scala class i just wanted to wrap this method in :

object ScalaClass{
  def toArray[T](t: java.lang.Class[T], coll: java.util.Collection[T]): Array[T] = {
    JavaClass.toArray[T](t, coll);
  }
}

compiling the Scala class gives me a very odd error :

error: type mismatch;

Any idea is appreciated.

EDIT 1: The full error message is

[ERROR] ScalaScala.scala:4002: error: type mismatch;
[INFO]  found   : Array[T]
[INFO]  required: Class[T]
[INFO]  JavaClass.toArray[T](t, coll);
[INFO]                      ^

Solution

  • Welcome to SO.

    This seems to compile, but I am not sure if it works with the real implementation of JavaClass, but I am pretty sure it should.

    import scala.reflect.ClassTag
    
    object ScalaClass {
      def toArray[T <: AnyRef](coll: java.util.Collection[T])(implicit ct: ClassTag[T]): Array[T] =
        JavaClass.toArray[T](ct.runtimeClass.asInstanceOf[Class[T]], coll)
    }
    

    To call it from Scala you just need to pass the java collection, it would get the correct class from the type, like this:

    val col: java.util.Collection[String] = ???
    
    val array: Array[String] = ScalaClass.toArray(col)
    

    Edit

    If you want to preserve the original signature, you have to do this:

    object ScalaClass {
      def toArray[T <: AnyRef](t: java.lang.Class[T], coll: java.util.Collection[T]): Array[T] =
        JavaClass.toArray[T](t, coll)
    }
    

    Which you can use like this:

    val col: java.util.Collection[String] = ???
    
    val array: Array[String] = ScalaClass.toArray(classOf[String], col)
    

    In both cases the trick is the <: AnyRef type bound.
    IMHO, the first version is more ergonomic to be used on idiomatic Scala code.