Согласование с шаблоном нескольких типов в Scala

80

Мне интересно, как я могу использовать сопоставление шаблонов нескольких типов. Я имею:

abstract class MyAbstract

case class MyFirst extends MyAbstract
case class MySecond extends MyAbstract
case class MyThird extends MyAbstract // shouldn't be matched and shouldn't call doSomething()

val x: MyAbstract = MyFirst

x match { 
 case a: MyFirst => doSomething()
 case b: MySecond => doSomething()
 case _ => doSomethingElse()
}

Поэтому я хотел бы написать что-то вроде:

x match {
 case a @ (MyFirst | MySecond) => doSomething()
 case _ => doSomethingElse()
}

Я видел похожую конструкцию в каком-то уроке, но это дает мне ошибку:

pattern type is incompatible with expected type;
[error]  found   : object MyFirst
[error]  required: MyAbstract

Итак, есть ли способ определить несколько разных типов в предложении case? Думаю, это сделало бы код красивее. Как будто у меня их будет 5, я напишу один и тот же код 5 раз (вызов doSomething ()).

Заранее спасибо!

Псисоев
источник
Я думаю, это проблема XY; у вас есть общий суперкласс для всех doSomethingслучаев, почему бы не сопоставить case a : MyAbstractthen ...?
Patryk 27wiek
Извините, забыл упомянуть, что у меня есть другие классы, которые расширяют класс MyAbstract и не должны вызывать doSomething.
псисоев
О, хорошо, просто хотел прояснить это :) Но теперь у вас есть правильный ответ на вашу проблему.
Patryk 27wiek
возможный дубликат классов Match multiple
case

Ответы:

135

Вам не хватает скобок для классов case. Классы case без списков параметров устарели.

Попробуй это:

abstract class MyAbstract
case class MyFirst() extends MyAbstract
case class MySecond() extends MyAbstract

val x: MyAbstract = MyFirst()


x match {
   case aOrB @ (MyFirst() | MySecond()) => doSomething(aOrB)
   case _ => doSomethingElse()
}

Если у вас слишком много параметров для ваших классов case и вам не нравится писать длинные Foo(_,_,..)шаблоны, то, возможно,:

x match {
   case aOrB @ (_:MyFirst | _:MySecond) => doSomething(aOrB)
   case _ => doSomethingElse()
}

Или просто:

x match {
   case _:MyFirst | _:MySecond => doSomething(x) // just use x instead of aOrB
   case _ => doSomethingElse(x)
}

Но, возможно, вам просто нужны одноэлементные объекты case?

abstract class MyAbstract
case object MyFirst extends MyAbstract
case object MySecond extends MyAbstract

val x: MyAbstract = MyFirst

x match {
   case aOrB @ (MyFirst | MySecond) => doSomething()
   case _ => doSomethingElse()
}
Файз
источник
1
И нет возможности избежать скобок? Поскольку у меня есть несколько параметров, и это становится некрасивым: case a @ (MyFirst ( , _, _, _, _) | MySecond ( , _, _, _, _)) => doSomething ()
psisoyev
9
Вы пропустили obj @ (_: MyFirst | _: MySecond)?
Жан-Филипп Пелле,
Нужен objв тех случаях, когда использую в doSomethingзвонке. В моем случае вызов doSomethingне использовался obj, поэтому он мне не нужен. Но в любом случае спасибо за комментарий!
псисоев
@ Jean-PhilippePellet Действительно, у меня есть. Разрешите мне отредактировать свой пост, чтобы добавить его.
Faiz
1
Было бы хорошо, если бы компиляция была достаточно умной, чтобы найти наиболее близкий общий тип вместо того, чтобы по умолчанию использовать тип ввода.
nilskp