Невозможно сопоставить параметризованный тип с конкретным типом после сопоставления с образцом

При использовании scala 2.12.8 это не скомпилируется без приведения:

trait Content
case object A extends Content
case class B(i: Int) extends Content

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(b) // does not compile
}
type mismatch;
 found   : b.type (with underlying type Playground.this.B)
 required: C

Вот ссылка Scastie на проблему: https://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ

Почему работает для объекта case, а не для класса case? Как я могу заставить его работать для класса case?

РЕДАКТИРОВАТЬ

Первые ответы заставили меня понять, что я упростил свою проблему, вот обновленная версия:

sealed trait Content
case object A extends Content
final case class B(i: Int) extends Content

sealed trait Container[+C <: Content]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

object Container {
  def apply[C <: Content](content: C): Container[C] = content match {
    case A => ContainerA(A) // compiles
    case b: B => ContainerB(b) // does not compile
  }
}

Ссылка на скасти: https://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ

C не может быть подтипом B, поскольку B является окончательным.


person M. Karassev    schedule 04.02.2019    source источник
comment
Замените List(b) на List(content)... но я не уверен, что этот метод делает то, что вы думаете...   -  person Lasf    schedule 04.02.2019


Ответы (3)


C не может быть подтипом B, поскольку B является окончательным.

Неправильный!

Одиночные типы экземпляров B являются подтипами B:

val b = B(0)
val container: Container[b.type] = Container[b.type](b)

Поскольку ContainerB не расширяет Container[b.type], его нельзя вернуть последней строкой. И его нельзя изменить так, чтобы он действовал;

case class ContainerB(content: B) extends Container[content.type]

не является законным в Scala.

Null также является подтипом B, и вы можете создать аналогичный пример. Как и типы уточнения, такие как B { type T = Int }.

Другие подтипы, которые, вероятно, не имеют значения, поскольку у них нет экземпляров: Nothing< /a>, составные типы, такие как B with Iterable[Int]...

person Alexey Romanov    schedule 05.02.2019
comment
Нет, из-за стирания типа все такие приведения становятся приведениями к Container[_] во время выполнения и завершатся успешно. - person Alexey Romanov; 05.02.2019

Решение дано в комментарии @lasf:

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(content) // compiles
}

Проблема в том, что возвращаемый тип — List[C], но компилятор не может гарантировать, что тип List(b) — это List[C]. В частности, C может быть подтипом B, и в этом случае List(b) будет List[B], что несовместимо с List[C].


Обновленную версию можно решить с помощью asInstanceOf, хотя это и некрасиво.

def apply[C <: Content](content: C): Container[C] = content match {
  case A => ContainerA(A) // compiles
  case b: B => ContainerB(b).asInstanceOf[Container[C]]
}

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

object Container {
  implicit def contain(content: A.type): Container[A.type] = ContainerA(content)
  implicit def contain(content: B): Container[B] = ContainerB(content)
}

val ca: Container[A.type] = A
val cb: Container[B] = B(0)

Или даже несколько конструкторов:

object Container {
  def apply(content: A.type): Container[A.type] = ContainerA(content)
  def apply(content: B): Container[B] = ContainerB(content)
}

Вот альтернативный дизайн с использованием класса типов. Это заменяет суперкласс Content классом типов Containable. Класс Container теперь может содержать что угодно, если для этого класса существует экземпляр Containable.

case object A
case class B(i: Int)

sealed trait Container[C]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

trait Containable[T] {
  def apply(value: T): Container[T]
}
object Containable {
  implicit object AContainer extends Containable[A.type] {
    def apply(value: A.type) = ContainerA(value)
  }
  implicit object BContainer extends Containable[B] {
    def apply(value: B) = ContainerB(value)
  }
}

object Container {
  def apply[C](content: C)(implicit containable: Containable[C]): Container[C] =
    containable(content)
}
person Tim    schedule 04.02.2019
comment
Извините, я упростил проблему, я просто отредактировал описание. Что касается вашего ответа, если я сделаю B окончательным, C не может быть подтипом B, но он не компилируется. - person M. Karassev; 04.02.2019
comment
На самом деле, я пытаюсь избежать этих приведений, а единственный конструктор обеспечивает большую факторизацию кода с моей стороны, но, возможно, я просто столкнулся с проблемой дизайна кода. - person M. Karassev; 05.02.2019
comment
@M.Karassev M.Karassev Я думаю, вам, вероятно, следует обратить внимание на использование классов типов< /а>. Я добавил пример того, как это может работать. - person Tim; 05.02.2019
comment
Спасибо, я действительно реорганизовал свой код, поэтому мне больше не нужны эти параметризованные типы. - person M. Karassev; 06.02.2019

Причина, по которой вы получаете сообщение об ошибке, заключается в том, что тип возвращаемого значения метода не является явным. При замене возвращаемого типа с List[C] на List[Content] проблема решается.

def asList[C <: Content](content: C): List[Content] = content match {
  case A => List(A) // compiles
  case b: B => List(b) // compiles
}
person Chaitanya Waikar    schedule 04.02.2019
comment
Это делает параметр типа бессмысленным, он также может быть def asList(content: Content): List[Content], и я сомневаюсь, что это то, чего хочет ОП. - person Tim; 04.02.2019
comment
Если это так, то почему мы используем TypeParameter C? Контент в любом случае является супертипом, поэтому мы можем поместить объект типа A и B в стек контента. Я не думаю, что это проблема OP. - person Raman Mishra; 04.02.2019
comment
Извините, я упростил проблему, я публикую редактирование как можно скорее, но я все же кое-что узнал из ответа Чайтаньи, спасибо! - person M. Karassev; 04.02.2019