Множественное наследование класса значений scala

У меня есть объекты проекта, которые представляют идентификаторы.

Допустим, это ChairId, TableId, LampId. Я хочу, чтобы все они наследовали от GenericId. И я хочу иметь возможность звонить def f(x: GenericId) = x.id

Я хочу, чтобы они содержали только один id: String, поэтому я хотел бы, чтобы они расширяли AnyVal.

Также я хотел бы, чтобы для каждого типа была предоставлена ​​функция generate, которая генерировала бы мой конкретный идентификатор, т.е. я хотел бы ввести что-то вроде ChairId.generate()

Я набрал это:

sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id

И я думал, что если бы GenericId наследовал AnyVal, это сработало бы, но пока безуспешно;/ Я также пытался сделать GenericId чертой и сделать классы case расширением AnyVal с помощью GenericId, но также не будет компилироваться:/

Еще одна вещь с TableId.generate(). Я могу предоставить объект-компаньон только с функцией generate, и это в основном решает мою проблему, но мне интересно, есть ли возможность решить это без определения объекта-компаньона? (т.е. как-то через имплициты)

// редактировать

относительно комментария для предоставления кода, который не компилируется (и я хотел бы):

sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)

person kpbochenek    schedule 31.07.2016    source источник
comment
Я не совсем уверен, что вы спрашиваете здесь. Если есть несколько вопросов, вы можете разделить их на несколько вопросов Stack Overflow.   -  person Chris Martin    schedule 31.07.2016
comment
Пожалуйста, включите код и ошибки компиляции для вещей, которые вы пробовали. Я думаю, это было бы полезно.   -  person Chris Martin    schedule 31.07.2016


Ответы (2)


Классы значений не могут работать таким образом по нескольким причинам.

Во-первых, из документации классы значений не могут быть расширены с помощью любой другой класс, поэтому AbstractId не может расширять AnyVal. (Ограничение №7)

scala> abstract class AbstractId(val id: String) extends AnyVal
<console>:10: error: `abstract' modifier cannot be used with value classes
       abstract class AbstractId(val id: String) extends AnyVal
                      ^

Во-вторых, даже если вы сделаете AbstractId свойством и определите другие идентификаторы следующим образом:

final case class DogId(val id: String) extends AnyVal with AbstractId

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

Класс значений фактически создается, когда:

  1. класс значений рассматривается как другой тип.
  2. класс значений присваивается массиву.
  3. выполнение тестов типа во время выполнения, таких как сопоставление с образцом.
person Michael Zajac    schedule 31.07.2016
comment
Ты опередил меня на несколько секунд :) - person pedrofurla; 31.07.2016

Некоторые цитаты из классов значений SIP, которые могут пояснить ваши сомнения:

Классы значений...

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

  2. ... не может быть расширен другим классом.

Согласно 1. он не может быть абстрактным; за 2. ваша кодировка не работает.

Есть еще одна оговорка:

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

Имея все это в виду, основываясь на вашем последнем фрагменте, это может сработать:

sealed trait AbstractId extends Any { def id: String }
final case class CatId(id: String) extends AnyVal with AbstractId
final case class DogId(id: String) extends AnyVal with AbstractId

Но имейте в виду, что выделение происходит только в том случае, если вы хотите использовать CatId и DogId в качестве AbstractId. Для лучшего понимания рекомендую прочитать SIP.

person pedrofurla    schedule 31.07.2016