Intereting Posts
Вложить только определенные параметры в конструктор Почему в этом методе Котлина есть обратные обратные линии? Android: Kotlin: пользовательский webView – не может быть вызван как функция. Функция 'invoke ()' не найдена Как высмеять функцию верхнего уровня в котлине с помощью jmockit Kotlin: Создайте и передайте истинные массивы Java (для JNA) Async Spring Boot с использованием Kotlin не работает Jacoco сообщает об 0 охвате классов Kotlin модульными тестами, в Android-проекте Задача Firebase еще не завершена Как найти, какой элемент не удалось сравнить между массивами в Котлине? Kotlin переименовывает сгенерированные геттеры и сеттеры Спецификация <K, V> введите в `Pair <K, V>` Невозможно получить доступ к переменной из внутреннего класса: Kotlin android Параллельные сопрограммы Kotlin Как расширить класс класса Kotlin или использовать общие для создания простого свойства getter, который будет работать во всех подклассах Number? intelliJ IDEA сгенерированный файл ant не выводит файлы

Как имитировать или достигать отношения IS-A для классов данных Kotlin

Я изучал Котлин и написал небольшую программу / скрипт, который выполняет задание, которое мне кажется скучным.

В разработке программы я использую классы данных для представления списка воспроизведения. в какой-то момент в дизайне я хотел иметь специальный тип Playlist который был EmptyPlaylist .

Я не мог заставить это работать.

Как бы вы достигли этих отношений с Котлином?

В Java я бы просто расширил список Playlist (или, возможно, создал интерфейс / абстрактный класс для их обоих, чтобы наследовать).

Я просто хотел иметь List<Playlist> а не List<Playlist?>

В конце концов, я просто создал объект Playlist , но меня интересует, можно ли создавать иерархии IS-A с классами данных.

UPD: Kotlin Beta добавил ограничения на классы data , поэтому ответ также был обновлен.

  • Классы данных не могут быть абстрактными, открытыми, закрытыми или внутренними;
  • Классы данных могут не распространять другие классы (но могут реализовывать интерфейсы).

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


Идея унаследовать EmptyPlaylist из EmptyPlaylist Playlist который считается непустым в соответствии с вашими словами, выглядит несколько противоречиво, но есть варианты:

  • Вы можете сделать как TracksPlaylist и EmptyPlaylist реализовать интерфейс Playlist . Это разумно, потому что EmptyPlaylist может не содержать все, что делает Playlist . Это будет выглядеть так:

     public interface Playlist data class TracksPlaylist(val name: String, val tracks: List<Track>) : Playlist data class EmptyPlaylist(val name: String): Playlist 
  • Если вы не хотите, чтобы несколько экземпляров EmptyPlaylist даже существовали, вы можете сделать это val вместо отдельного класса и избежать проверок, просто сравнивая с

     val EMPTY_PLAYLIST = TracksPlaylist("EMPTY", listOf()) 

    Это применимо, когда пустые списки воспроизведения равны, так что одного объекта достаточно, чтобы представить их все.

  • Вы можете использовать sealed классы . Это не позволяет использовать классы data , но sealed классы могут соответствовать для построения иерархии классов, подобных этому.

    sealed класс – это абстрактный класс, который может только объявлять его подклассы внутри своего тела. Это дает компилятору гарантии, что они являются единственными подклассами. Пример:

     sealed class Playlist(val name: String) { class Tracks(name: String, val tracks: List<Track>): Playlist(name) class Playlists(name: String, val innerPlaylists: List<Playlist>): Playlist(name) object Empty: Playlist("EMPTY") } 

    После этого вы сможете использовать оператор if без указания ветки else если вы укажете все подклассы и объекты закрытого класса:

     when (p) { is Playlist.Tracks -> { /* ... */ } is Playlist.Playlists -> { /* ... */ } Playlist.Empty -> { /* ... */ } } 

    Однако эта опция требует, чтобы вы hashCode дело с equals , hashCode , toString , copy и componentN самостоятельно, если они вам нужны.