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!
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.