2016-08-26 2 views
1

Пытаюсь следующее:саз Scala неудачи

def getStatus: MyStatus = { 
    (mPending, mPublished) match { 
    case (None, None) => MyStatus.inactive 
    case (pending: Option[Edit], None) => MyStatus.neverPublished 
    case (None, published: Option[Edit]) => if (published.get.isSuspended) MyStatus.suspended else MyStatus.published 
    case (pending: Option[Edit], published: Option[Edit]) => 
     if (published.get.isSuspended) 
     MyStatus.suspendedWithChanges 
     else 
     MyStatus.publishedWithChanges 
    } 
} 

Насколько я могу видеть, если он получает до последнего case, ни Option не должна быть None, но я получаю следующее:

play.api.UnexpectedException: Unexpected exception[ClassCastException: null] 
.... 
Caused by: java.lang.ClassCastException: scala.None$ cannot be cast to com.fredley.Edit 

выброшено на if (published.get.isSuspended). Что происходит?

+1

Вы создаете тип, попробуйте удалить часть ': Option [Edit]', возможно, вы не получаете тип, который вы ожидаете, если эти переменные являются параметрами в вашем совпадении, должно быть 'None' и 'Some (value)', если у вас нет опции. Для исключения это происходит при получении из-за того, что вы соответствуете 'Option [Edit]', но 'None' является допустимым для этого типа. –

+1

Чтобы перефразировать Ende Neu, используйте 'Some (edit: Edit)', чтобы извлечь непустую опцию. Но странный CCE предполагает, что у вас есть вложенный вариант. –

+0

Является ли 'MyStatus' псевдоним' (опция [Edit], Option [Edit]) => Something'? – pedrofurla

ответ

2

Примечание: @ Ответ Алексея Романова, приведенный ниже, является правильным. Я обновил этот ответ, чтобы включить более глубокую и правильную информацию.

Опция, некоторые и None

Вы можете лучше понять, как Option работает с более простым примером. Здесь, Option, что на самом деле None проходит первый случай и улавливается только на втором уровне:

val foo: Option[String] = None 

foo match { 
    case x: Option[String] => "Yep! It's an option!" 
    case None => "Foo is None!" 
} // res0: String = Yep! It's an option! 

Поскольку None на самом деле подкласс Option, значение None будет соответствовать Option[String]. Правильное решение заключается в использовании Some(String):

foo match { 
    case Some(String) => "It's a string!" 
    case None => "Foo is None!" 
} // res0: String = Foo is None! 

это только фон, но: кажется, у вас есть вложенная Option:

scala.None $ не может быть приведен к com.fredley.Edit

Это указывает на то, что оба pending и published не None, и действительно Option[<something>], но из-за типа стиранием матч не диффере tiate. Например:

val foo: Option[String] = None 
val bar: Option[String] = Option("fubar") 

for (x <- List(foo, bar)) x match { 
    case x: Option[Int] => println(s"It's a string=$x") 
    case None => println(s"It is None!") 
    case _ => println("No match found.") 
} 

Даже если это не должно соответствовать Option[Int], выше будет печатать:

It's a string=None 
It's a string=Some(fubar) 

Примечание на условные переменные

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

val suspended = true 
foo match { 
    case x: Some[String] => "It's a string!" 
    case None if suspended => "None but suspended!" 
    case None => "Foo is None!" 
} // res0: String = None but suspended! 

Таким образом, вы можете преобразовать ваш код:

def getStatus: MyStatus = { 
    (mPending, mPublished) match { 
    case (None, None) => MyStatus.inactive 
    case (Some(Edit), None) => MyStatus.neverPublished 
    case (None, Some(Edit)) if published.get.isSuspended => MyStatus.suspended 
    case (None, Some(Edit)) => MyStatus.published 
    case (_, Some(published)) if published.get.isSuspended => MyStatus.suspendedWithChanges // We don't care about the first param, so use _, let the compiler determine the type of `published`. 
    case (_, Some(published)) => MyStatus.publishedWithChanges 
    } 
} 

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

+0

Несмотря на согласие, я категорически не согласен с этим ответом. 1. В этом примере вы не видите, что происходит в вопросе, потому что он меняет порядок: сначала вопрос совпадает с «Нет». 2. Использование ': Some [Edit]' pattern - всего лишь плохая идея. –

+0

@AlexeyRomanov Казалось, что недоразумение было вокруг не понимания того, что 'None' все еще является« опцией »; пример был построен, чтобы продемонстрировать это, в частности - но я понимаю, что вы говорите, и это действительная точка. Я признаю, что я не совсем понимаю, почему:: Некоторые [Edit] 'это« плохая идея », хотя: можете ли вы сказать два слова, почему? –

+0

Если бы это было непонимание, я бы не ожидал увидеть «если он попадает в последний случай», ни один вариант не должен быть «Нет» в вопросе. На второй, см. Мой ответ. –

1

Использование Some вместо Option должно работать.

def getStatus: MyStatus = { 
    (mPending, mPublished) match { 
     case (None, None) => MyStatus.inactive 
     case (Some(pending: Edit), None) => MyStatus.neverPublished 
     case (None, Some(published: Edit) => if (published.get.isSuspended) MyStatus.suspended else MyStatus.published 
     case (Some(pending: Edit), Some(published:Edit)) => 
      if (published.get.isSuspended) 
      MyStatus.suspendedWithChanges 
      else 
      MyStatus.publishedWithChanges 
    } 
    } 
2

Другие два текущих ответа, к сожалению, не объясняют настоящую проблему.

Если mPending и mPublished имеют тип Option[Edit], ваш код должен работать, несмотря на то, что он очень унииоматичен. Но из-за стирания типа published: Option[Edit] может только проверить, что published - это Option (при компиляции вы должны получить предупреждение о параметре unchecked type!). Поскольку первые две строки исключают, что он равен None (по крайней мере, если mPending является Option), это Some, и поэтому published.get возвращает результат. Чтобы вызвать isSuspended по этому результату, Scala должен направить его на Edit. Но из сообщения об исключении это выглядит так: None; то есть mPublished - Some(None), и его реальный тип - это что-то вроде Option[Option[Edit]] (это может быть что-то вроде Option[Any]).

Теперь, учитывая все это, правильное решение близко к @ TheKojuEffect'S:

def getStatus: MyStatus = { 
    (mPending, mPublished) match { 
    case (None, None) => MyStatus.inactive 
    case (_, None) => MyStatus.neverPublished 
    case (None, Some(published)) => if (published.isSuspended) MyStatus.suspended else MyStatus.published 
    case (_, Some(published)) => 
     if (published.isSuspended) 
     MyStatus.suspendedWithChanges 
     else 
     MyStatus.publishedWithChanges 
    } 
} 

То есть, вы должны использовать Some(published) шаблон, но пусть Scala выяснить, что тип published является. Если предположение, приведенное выше, верное, компилятор обнаружит, что он равен Option[Edit] (или Any) и говорит, что он не имеет метода isSuspended. После этого вы можете выяснить, как исправить код: изменить линии над совпадением, чтобы сделать mPublished вместо Option[Edit]? Измените шаблон на Some(Some(published))? Что-то другое?

+0

@TomMedley Это самый правильный ответ, и его следует принять (imo). –