Могу ли я выполнить сопоставление параметра типа в Scala, чтобы увидеть, реализует ли он черту? - PullRequest
16 голосов
/ 30 марта 2011

Я хотел бы иметь метод, который возвращает класс определенного типа, но я хочу, чтобы метод вел себя по-разному в зависимости от того, расширяет ли класс конкретную черту следующим образом:


case class ClassA extends TraitA
case class ClassB extends TraitB
case class ClassC extends TraitA
...
def myfunc[T]():T = {
  T match {
    case TraitA => // return new T in a particular way 
    case TraitB => // ditto
  }
}

Возможно ли это, или я поступаю неправильно?

Спасибо

Ответы [ 3 ]

14 голосов
/ 30 марта 2011

Вы не можете сравнивать типы напрямую, потому что там не с чем сравнивать (во время выполнения из-за erasure ).Вы можете работать с представлением вашего класса:

trait TraitA { }
trait TraitB { }
class ClassA extends TraitA { }
class ClassB extends TraitB { }

def myFunc[T](clazz: Class[T]) = {
  if (classOf[TraitA] isAssignableFrom clazz) println("A")
  else if (classOf[TraitB] isAssignableFrom clazz) println("B")
  else println("?")
}

scala> myFunc(classOf[ClassA])
A

scala> myFunc(classOf[String])
?

или вы можете сопоставлять шаблоны на экземплярах класса:

def myFunc2[T](t: T) = t match {
  case _: TraitA => println("A")
  case _: TraitB => println("B")
  case _ => println("?")
}

scala> myFunc2(new ClassA)
A

scala> myFunc2(Some(5))
?

Вы также можете использовать первый подход в синтаксически меньшенавязчивый путь через манифесты класса:

def myFunc3[T](implicit mf: ClassManifest[T]) = {
  val clazz = mf.erasure
  if (classOf[TraitA] isAssignableFrom clazz) println("A")
  else if (classOf[TraitB] isAssignableFrom clazz) println("B")
  else println("?")
}

scala> myFunc3[ClassA]
A

scala> myFunc3[String]
?

, и вы можете выбирать различные виды отправки, даже если if / else становится громоздким:

object MyFunc {
  val dispatch = Map(
    classOf[TraitA] -> (() => println("A")),
    classOf[TraitB] -> (() => println("B"))
  )
  val default = () => println("?")
  def apply[T](implicit mf: ClassManifest[T]) = 
    dispatch.find(_._1 isAssignableFrom mf.erasure).map(_._2).getOrElse(default)()
}

scala> MyFunc[ClassA]
A

scala> MyFunc[String]
?

Обратите внимание, что любой универсальный код, который вы используете, этоfrom должен иметь доступный манифест класса (либо в качестве неявного параметра, либо в сокращенном виде, [T: ClassManifest].

2 голосов
/ 30 марта 2011

"хочу, чтобы метод вел себя по-разному в зависимости от того, расширяет ли класс определенную черту"

Это почти каноническое описание наследования. Разве у вас не может быть метода в каждой черте, который инкапсулирует отличающееся поведение, к которому вы стремитесь?

1 голос
/ 30 марта 2011

Вам нужен экземпляр для проверки типа. Обобщенный сам по себе является просто заполнителем для типа. Вы могли бы сделать что-то вроде этого:

trait bTrait //common base trait
trait TraitA extends bTrait
trait TraitB extends bTrait

class ClassA extends TraitA
class ClassB extends TraitB

def myFunc[T <: bTrait](t:T) : String = //passing explicitly an instance of type T
{
  t match {
    case _ : TraitA => "TraitA"
    case _ : TraitB => "TraitB" 
  }
}

println(myFunc(new ClassA)) //prints TraitA
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...