summaryrefslogtreecommitdiff
path: root/src/library/scala/reflect/ClassTag.scala
blob: e42c89d21fe2c525a893c81c671e772350efcb91 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package scala.reflect

import java.lang.{ Class => jClass }
import language.{implicitConversions, existentials}
import scala.runtime.ScalaRunTime.{ arrayClass, arrayElementClass }

/** A `ClassTag[T]` wraps a runtime class, which can be accessed via the `runtimeClass` method.
 *
 *  This is useful in itself, but also enables very important use case.
 *  Having this knowledge ClassTag can instantiate `Arrays`
 *  in those cases where the element type is unknown at compile time.
 *
 *  If an implicit value of type u.ClassTag[T] is required, the compiler will make one up on demand.
 *  The implicitly created value contains in its `runtimeClass` field the runtime class that is the result of erasing type T.
 *  In that value, any occurrences of type parameters or abstract types U which come themselves with a ClassTag
 *  are represented by the type referenced by that tag.
 *  If the type T contains unresolved references to type parameters or abstract types, a static error results.
 *
 * @see [[scala.reflect.base.TypeTags]]
 */
@annotation.implicitNotFound(msg = "No ClassTag available for ${T}")
trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable {
  // please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder`
  // class tags, and all tags in general, should be as minimalistic as possible

  /** A class representing the type `U` to which `T` would be erased.
   *  Note that there is no subtyping relationship between `T` and `U`.
   */
  def runtimeClass: jClass[_]

  /** Produces a `ClassTag` that knows how to build `Array[Array[T]]` */
  def wrap: ClassTag[Array[T]] = ClassTag[Array[T]](arrayClass(runtimeClass))

  /** Produces a new array with element type `T` and length `len` */
  override def newArray(len: Int): Array[T] =
    runtimeClass match {
      case java.lang.Byte.TYPE      => new Array[Byte](len).asInstanceOf[Array[T]]
      case java.lang.Short.TYPE     => new Array[Short](len).asInstanceOf[Array[T]]
      case java.lang.Character.TYPE => new Array[Char](len).asInstanceOf[Array[T]]
      case java.lang.Integer.TYPE   => new Array[Int](len).asInstanceOf[Array[T]]
      case java.lang.Long.TYPE      => new Array[Long](len).asInstanceOf[Array[T]]
      case java.lang.Float.TYPE     => new Array[Float](len).asInstanceOf[Array[T]]
      case java.lang.Double.TYPE    => new Array[Double](len).asInstanceOf[Array[T]]
      case java.lang.Boolean.TYPE   => new Array[Boolean](len).asInstanceOf[Array[T]]
      case java.lang.Void.TYPE      => new Array[Unit](len).asInstanceOf[Array[T]]
      case _                        => java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]]
    }

  /** A ClassTag[T] can serve as an extractor that matches only objects of type T.
   *
   * The compiler tries to turn unchecked type tests in pattern matches into checked ones
   * by wrapping a `(_: T)` type pattern as `ct(_: T)`, where `ct` is the `ClassTag[T]` instance.
   * Type tests necessary before calling other extractors are treated similarly.
   * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)`
   * is uncheckable, but we have an instance of `ClassTag[T]`.
   */
  def unapply(x: Any): Option[T] = unapply_impl(x)
  def unapply(x: Byte): Option[T] = unapply_impl(x)
  def unapply(x: Short): Option[T] = unapply_impl(x)
  def unapply(x: Char): Option[T] = unapply_impl(x)
  def unapply(x: Int): Option[T] = unapply_impl(x)
  def unapply(x: Long): Option[T] = unapply_impl(x)
  def unapply(x: Float): Option[T] = unapply_impl(x)
  def unapply(x: Double): Option[T] = unapply_impl(x)
  def unapply(x: Boolean): Option[T] = unapply_impl(x)
  def unapply(x: Unit): Option[T] = unapply_impl(x)

  private def unapply_impl[U: ClassTag](x: U): Option[T] =
    if (x == null) None
    else {
      val staticClass = classTag[U].runtimeClass
      val dynamicClass = x.getClass
      val effectiveClass = if (staticClass.isPrimitive) staticClass else dynamicClass
      val conforms = runtimeClass.isAssignableFrom(effectiveClass)
      if (conforms) Some(x.asInstanceOf[T]) else None
    }

  /** case class accessories */
  override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]]
  override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass
  override def hashCode = scala.runtime.ScalaRunTime.hash(runtimeClass)
  override def toString = {
    def prettyprint(clazz: jClass[_]): String =
      if (clazz.isArray) s"Array[${prettyprint(arrayElementClass(clazz))}]" else
      clazz.getName
    prettyprint(runtimeClass)
  }
}

object ClassTag {
  private val ObjectTYPE = classOf[java.lang.Object]
  private val NothingTYPE = classOf[scala.runtime.Nothing$]
  private val NullTYPE = classOf[scala.runtime.Null$]

  val Byte    : ClassTag[scala.Byte]       = Manifest.Byte
  val Short   : ClassTag[scala.Short]      = Manifest.Short
  val Char    : ClassTag[scala.Char]       = Manifest.Char
  val Int     : ClassTag[scala.Int]        = Manifest.Int
  val Long    : ClassTag[scala.Long]       = Manifest.Long
  val Float   : ClassTag[scala.Float]      = Manifest.Float
  val Double  : ClassTag[scala.Double]     = Manifest.Double
  val Boolean : ClassTag[scala.Boolean]    = Manifest.Boolean
  val Unit    : ClassTag[scala.Unit]       = Manifest.Unit
  val Any     : ClassTag[scala.Any]        = Manifest.Any
  val Object  : ClassTag[java.lang.Object] = Manifest.Object
  val AnyVal  : ClassTag[scala.AnyVal]     = Manifest.AnyVal
  val AnyRef  : ClassTag[scala.AnyRef]     = Manifest.AnyRef
  val Nothing : ClassTag[scala.Nothing]    = Manifest.Nothing
  val Null    : ClassTag[scala.Null]       = Manifest.Null

  def apply[T](runtimeClass1: jClass[_]): ClassTag[T] =
    runtimeClass1 match {
      case java.lang.Byte.TYPE      => ClassTag.Byte.asInstanceOf[ClassTag[T]]
      case java.lang.Short.TYPE     => ClassTag.Short.asInstanceOf[ClassTag[T]]
      case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]]
      case java.lang.Integer.TYPE   => ClassTag.Int.asInstanceOf[ClassTag[T]]
      case java.lang.Long.TYPE      => ClassTag.Long.asInstanceOf[ClassTag[T]]
      case java.lang.Float.TYPE     => ClassTag.Float.asInstanceOf[ClassTag[T]]
      case java.lang.Double.TYPE    => ClassTag.Double.asInstanceOf[ClassTag[T]]
      case java.lang.Boolean.TYPE   => ClassTag.Boolean.asInstanceOf[ClassTag[T]]
      case java.lang.Void.TYPE      => ClassTag.Unit.asInstanceOf[ClassTag[T]]
      case ObjectTYPE               => ClassTag.Object.asInstanceOf[ClassTag[T]]
      case NothingTYPE              => ClassTag.Nothing.asInstanceOf[ClassTag[T]]
      case NullTYPE                 => ClassTag.Null.asInstanceOf[ClassTag[T]]
      case _                        => new ClassTag[T]{ def runtimeClass = runtimeClass1 }
    }

  def unapply[T](ctag: ClassTag[T]): Option[Class[_]] = Some(ctag.runtimeClass)
}