Scala — Использование сложных обобщений для получения информации о подклассе и другом классе — не компилируется?

#scala #generics #inheritance #subclass

#scala #обобщения #наследование #подкласс

Вопрос:

Я пишу программу на scala, которая использует фреймворк вокруг:

 trait Tool[T <: Tool[T, U], U <: Settings[T]] {
  // members here
  def createSettingsFrom(settingsWithStringNames: Map[String, _]): U
}

trait Settings[T <: Tool[T, _ <: Settings[T]]
  

В Tool T — это подкласс, а U — класс, который несет информацию для него. Каждый инструмент можно рассматривать как своего рода команду с параметрами, и эти параметры являются пользовательскими для каждого из них.

У меня также есть класс, который расширяет его вместе с его «носителем информации»:

 object Cleanup extends Tool[Cleanup, CleanupSettings] {
  override def createSettingsFrom(settings: Map[String, _]): CleanupSettings 
    = CleanupSettings(
      settings.get("attribute1").asInstanceOf[Int]
      settings.get("attribute2").asInstanceOf[String])
}

case class CleanupSettings extends Settings[Cleanup](
  //attribute1: Int,
  //attribute2: String
  //more attributes)
  

Когда я пытаюсь скомпилировать эти классы, я получаю следующий stacktrace:

Information:21/10/16 03:20 - Compilation completed with 2 errors and 0 warnings in 3s 200ms
/project_folder/src/main/scala/io/oreville/maptools/operations/cleanup/Cleanup.scala
Error:(17, 24) type arguments [package.tools.operations.cleanup.Cleanup,package.tools.operations.cleanup.CleanupSettings] do not conform to trait ConfigurableTool's type parameter bounds [T <: package.tools.ConfigurableTool[T,U],U <: package.tools.params.Settings[T]]
object Cleanup extends ConfigurableTool[Cleanup, CleanupSettings] {
^
/project_folder/src/main/scala/io/oreville/maptools/operations/cleanup/CleanupSettings.scala
Error:(11, 11) type arguments [package.tools.operations.cleanup.Cleanup] do not conform to trait Settings's type parameter bounds [T <: package.tools.Tool[T, _ <: package.tools.params.Settings[T]]]
extends Settings[Cleanup]
^

У меня также есть trait ConfigurableTool, который является просто расширением инструмента с некоторой дополнительной функциональностью, поэтому он имеет точно такую же общую подпись, и это просто extends Tool[T, U] .

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

Вот и все, я надеюсь, что у вас есть немного времени, чтобы помочь моему делу, если нет, все равно спасибо за чтение!

Ответ №1:

Я не могу воспроизвести полученное вами сообщение об ошибке.

В коде есть несколько опечаток, но после их устранения осталась единственная ошибка — в определении Cleanup , которое вы не можете передать в качестве параметра типа при расширении признака.

 object Cleanup extends Tool[Cleanup.type, CleanupSettings] { ... }
  

незаконная циклическая ссылка, включающая очистку объекта

Вы можете обойти это, сделав это признаком и расширив его в сопутствующем объекте. Вот полный код:

 trait Tool[T <: Tool[T, U], U <: Settings[T]] {
  // members here
  def createSettingsFrom(settingsWithStringNames: Map[String, _]): U
}

trait Settings[T <: Tool[T, _ <: Settings[T]]]

trait Cleanup extends Tool[Cleanup, CleanupSettings] {
  override def createSettingsFrom(settings: Map[String, _]): CleanupSettings = CleanupSettings(
    settings.get("attribute1").asInstanceOf[Int],
    settings.get("attribute2").asInstanceOf[String])
}
object Cleanup extends Cleanup

case class CleanupSettings(
  attribute1: Int,
  attribute2: String) extends Settings[Cleanup]
  

Комментарии:

1. О, это очень круто. В итоге я удалил параметр типа подкласса, чтобы упростить его. Я понял, что ссылаться на очистку — это не то же самое, что ссылаться на его класс, но это хороший обходной путь для циклической ссылки. Спасибо 🙂