code
code

Reputation: 5642

How to determine if a class is a subclass of a parent class or trait?

In Scala, how can we determine if a class is a subclass of a parent class or a trait? For example:

trait MyTrait
class MyParentClass()
class MyOtherParentClass()

case class MySubClass() extends MyParentClass with MyTrait
case class MyOtherSubClass() extends MyOtherParentClass

Is it possible to identify if class such as MySubClass extends from MyParentClass or MyTrait without instantiating an object and through reflection APIs? Given an unknown generic type T, I am interested in having it match a case if T extends a particular parent class or a trait:

def example[T](): Unit = {
    T match {
      case if T extends MyParentClass => ...
      case if T extends MyOtherParentClass => ...
      case if T extends MyOtherTrait => ...
      case _ => default case ...
}

Upvotes: 0

Views: 1286

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51658

If you write

case class MySubClass() extends MyParentClass with MyTrait

then obviously MySubClass extends MyParentClass and MyTrait so you can check that for a generic type T with

def test[T](implicit ev: T <:< MyParentClass, ev1: T <:< MyTrait) = ???

test[MySubClass] // compiles

at compile time.


If the thing is you want to check that with OR instead of AND, then you can use shapeless.OrElse or implicitbox.Priority

Scala method that needs either one of two implicit parameters


I updated the question with an example of the desired usage

It seems you want a type class

trait Example[T] {
  def example(): Unit
}
object Example {
  implicit def subtypeOfMyParentClass[T <: MyParentClass] = new Example[T] {
    override def example(): Unit = ???
  }

  implicit def subtypeOfMyOtherParentClass[T <: MyOtherParentClass] = new Example[T] {
    override def example(): Unit = ???
  }

  implicit def subtypeOfMyOtherTrait[T <: MyOtherTrait] = new Example[T] {
    override def example(): Unit = ???
  }

  implicit def default[T] = new Example[T] {
    override def example(): Unit = ???
  }
}

def example[T]()(implicit e: Example[T]): Unit = e.example()

A type class is a compile-time (i.e. type-level) replacement for pattern matching.

If there is ambiguity among implicits you can prioritize them.


Just curious, do you know if there is a way to do this in a simple one line conditional such as if (T extends from MyParentClass) then ... through reflection APIs (is it possible through classOf[] or typeOf[]?)

You can do that at runtime

import scala.reflect.runtime.universe._

def example[T: TypeTag](): Unit = 
  if (typeOf[T] <:< typeOf[MyParentClass]) ???
  else if (typeOf[T] <:< typeOf[MyOtherParentClass]) ???
  else if (typeOf[T] <:< typeOf[MyOtherTrait]) ???
  else ???

or

import scala.reflect.ClassTag

def example[T: ClassTag](): Unit = 
  if (classOf[MyParentClass] isAssignableFrom classOf[T]) ???
  else if (classOf[MyOtherParentClass] isAssignableFrom classOf[T]) ???
  else if (classOf[MyOtherTrait] isAssignableFrom classOf[T]) ???
  else ???

or at compile time

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def example[T](): Unit = macro exampleImpl[T]

def exampleImpl[T: c.WeakTypeTag](c: blackbox.Context)(): c.Tree = {
  import c.universe._

  if (weakTypeOf[T] <:< typeOf[MyParentClass]) ???
  else if (weakTypeOf[T] <:< typeOf[MyOtherParentClass]) ???
  else if (weakTypeOf[T] <:< typeOf[MyOtherTrait]) ???
  else ???
}

But implicits and types is a preferable way rather than (compile-time or especially runtime) reflection. It's not clear why you need reflection at all.

https://users.scala-lang.org/t/how-to-access-the-method/6281

Upvotes: 5

Related Questions