4

Given classes

sealed abstract class A

case class B(param: String) extends A

case class C(param: Int) extends A

trait Z {}

class Z1 extends Z {}

class Z2 extends Z {}

def zFor[T <: A : Manifest]: Option[Z] = {
  val z = manifest[T].erasure
  if (z == classOf[B]) {
    Some(new Z1)
  } else
  if (z == classOf[C]) {
    Some(new Z2)
  } else {
    None
  }
}

I think the problem with pattern matching here is impossibility to build pattern matching table in the bytecode. Is there any workaround over this problem? May be I can use some Int generated in Manifest by compiler?

jdevelop
  • 12,176
  • 10
  • 56
  • 112
  • Did you see any sort of issue here? I ran this code and it worked as expected. You should change `erasure` to `runtimeClass` (if using Scala 2.10`, but it worked both ways. – cmbaxter May 19 '13 at 10:41

2 Answers2

2

The way you've written it there isn't very robust if you have a more complicated class hierarchy since if you have a class D <: C, then classOf[D] != classOf[C]. So you don't really want to pattern-match anyway. But you could; you can't call classOf[X] in the middle of a pattern-match, but you can

def zFor[T <: A : Manifest]: Option[Z] = {
  val ClassB = classOf[B]
  val ClassC = classOf[C]
  manifest[T].erasure match {
    case ClassB => Some(new Z1)
    case ClassC => Some(new Z2)
    case _      => None
  }
}

as long as you're sure you're in a situation where you only need to detect the leaves of the class hierarchy. (You should probably make sure by marking B and C final.)

Alternatively, you can use isAssignableFrom to perform a runtime test:

def zFor2[T <: A : Manifest]: Option[Z] = {
  manifest[T].erasure match {
    case x if classOf[B].isAssignableFrom(x) => Some(new Z1)
    case x if classOf[C].isAssignableFrom(x) => Some(new Z2)
    case _                                   => None
  }
}

and now

class D extends C(5) {}

scala> zFor[D]
res5: Option[Z] = None

scala> zFor2[D]
res6: Option[Z] = Some(Z2@2556af33)
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
1

I am not sure if this fits your problem (as you have probably shown a simplified example). But this kind of functionality can be created without using reflection.

It is quite a common pattern that allows you to create add more combinations without modifying the original code.

// the - here allows D to return the instance for C
// if you only want exact matches, remove the -
trait GetZ[-X] {
  type Out
  def z: Option[Out]
}

trait LowerPriority {
  implicit def noZ[A] =
    new GetZ[A] {
      type Out = Nothing
      val z = None
    }
}

object GetZ extends LowerPriority {
  implicit def forB =
    new GetZ[B] {
      type Out = Z1
      def z = Some(new Z1)
    }

  implicit def forC =
    new GetZ[C] {
      type Out = Z2
      def z = Some(new Z2)
    }
}

def zFor[T](implicit getZ: GetZ[T]): Option[getZ.Out] = getZ.z

Usage

class D extends C(5)

val noZ = zFor[A]
val Some(z1) = zFor[B]
val Some(z2) = zFor[C]
val Some(other_z2) = zFor[D]
EECOLOR
  • 11,184
  • 3
  • 41
  • 75