Get ClassTag from within specialized class

2019-05-14 18:02发布

问题:

When I have a @specialized class, I retain the information about what primitive my type is. Is there a fast way to get a ClassTag[T], e.g. to create arrays?

This does not compile, because no ClassTag[T] is available

class Foo[@specialized T] {
  def bar: Array[T] = new Array[T]
}

This works, but I want to avoid passing the ClassTag

class Foo[@specialized T: ClassTag] {
  def bar: Array[T] = new Array[T]
}

This works, but is slow:

class Foo[@specialized T] {
  def bar: Array[T] = new Array[T]

  implicit def classTag: ClassTag[T] = {
    val name = getClass.getName
    if(name.endsWith("$I$sp") ClassTag.Int
    else if(name.endsWith("$L$sp") ClassTag.Long
    else ??? // you get the idea
  }
}

回答1:

Specialization happens later than materialization of ClassTags at typer, so there's the matter of the barn door.

The other issue is that specialization will create double definitions, so it's not trivial to hand-specialize a method in the obvious way.

Here's an attempt to mix-in the specialized method that provides a class tag.

Specialized f calls specialized ct which is overridden in the trait.

I don't know that the dummy arg to ct is necessary to make it specialize; maybe the ClassTag[A] result type isn't enough.

$ scalam
Welcome to Scala 2.12.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.

scala> import reflect.ClassTag
import reflect.ClassTag

scala> class CT[@specialized(Int) A] {
     |   def ct(a: A): ClassTag[A] = ???
     |   def f: Array[A] = ct(null.asInstanceOf[A]).newArray(1)
     | }
defined class CT

scala> trait CTS { def ct$mcI$sp(i: Int): ClassTag[Int] = reflect.classTag[Int] }
defined trait CTS

scala> (new CT[Int] with CTS).f
res0: Array[Int] = Array(0)

Showing specialized invocation:

scala> :javap -c -
Compiled from "<console>"
[snip]
      16: invokevirtual #32                 // Method $line16/$read$$iw$$iw$$anon$1.f$mcI$sp:()[I
      19: putfield      #24                 // Field res0:[I