javascalatypesjvmclasstag

Scala - How to Convert Generic List To Array and avoid "No ClassTag available for T"


I am working on what should be a trivial piece of code. I want to take a List, and convert it to an equivalent Array:

import scala.collection.JavaConverters

object Help extends App {
  def f1[T](lst: List[T]) = {
    lst.toArray
  }

  val x = List(1, 2, 3)
  println(f1(x))
}

Running this code gives:

"No ClassTag available for T"

How can I avoid this problem; I am coming from a Python background so would appreciate a response that gives me an understanding of the mechanics here.

Thank you!


Solution

  • Try adding implicit ClassTag parameter like so

    import scala.reflect.ClassTag
    
    def f1[T](lst: List[T])(implicit ev: ClassTag[T]) = {
      lst.toArray
    }
    

    The reason is clear if we have a look at signature of toArray

    def toArray[B >: A: ClassTag]: Array[B]
    

    which is equivalent to

    def toArray[B >: A](implicit ev: ClassTag[B]): Array[B]
    

    so you have to thread the implicit parameter from f1 down to toArray.

    would appreciate a response that gives me an understanding of the mechanics here

    The key is to understand Array is not a true Scala collection. For once, it is not a subtype of Iterable

    implicitly[Array[Int] <:< Iterable[Int]] // error
    

    Another difference between Array and true Scala collections is the absence of need for ClassTag for true Scala collections. The reason for this is Scala collections go through a process of type erasure which means runtime knows it is a List but does not differentiate between List[Int] and List[String], for example. However this does not hold for Array. At runtime there is indeed a difference between Array[Int] and Array[String]. Hence ClassTag is a mechanism invented to carry type information, which exists only at compile-time, over to runtime, so Array can have the behaviour as in Java.

    You will encounter other differences, for example

    Array(42) == Array(42)  // false
    List(42) == List(42)    // true
    

    Best practice is to avoid Array if possible.