Что такое закрытые занятия в Котлине? - PullRequest
0 голосов
/ 09 июня 2018

Я новичок в Котлине и недавно прочитал о Запечатанных классах .Но из документа единственное, что я на самом деле думаю, это то, что они существуют.

В документе говорится, что они "представляют ограниченную иерархию классов".Кроме того, я нашел утверждение, что они являются сверхспособностями.Оба аспекта на самом деле не ясны.

Так что вы можете помочь мне ответить на следующие вопросы:

  • Что такое запечатанные классы и как их использовать идиоматически?
  • Имеется ли такая концепция в других языках, таких как Python, Groovy или C #?

ОБНОВЛЕНИЕ: Я тщательно проверил это сообщение в блоге и до сих пор могу 'обернуть голову вокруг этой концепции.Как указано в сообщении

Преимущество

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

Почему компилятор не знает о других определенных подклассахв других файлах?Даже IDE знает это.Просто нажмите Ctrl+Alt+B в IDEA, например, на определение List<>, и все реализации будут показаны даже в других исходных файлах.Если подкласс может быть определен в какой-то сторонней среде, которая не используется в приложении, почему мы должны заботиться об этом?

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

Если вы когда-либо использовали enum с abstract method просто для того, чтобы вы могли сделать что-то вроде этого:

public enum ResultTypes implements ResultServiceHolder {
    RESULT_TYPE_ONE {
        @Override
        public ResultOneService getService() {
            return serviceInitializer.getResultOneService();
        }
    },
    RESULT_TYPE_TWO {
        @Override
        public ResultTwoService getService() {
            return serviceInitializer.getResultTwoService();
        }
    },
    RESULT_TYPE_THREE {
        @Override
        public ResultThreeService getService() {
            return serviceInitializer.getResultThreeService();
        }
    };

Когда в действительности вы хотели бы это:

val service = when(resultType) {
    RESULT_TYPE_ONE -> resultOneService,
    RESULT_TYPE_TWO -> resultTwoService,
    RESULT_TYPE_THREE -> resultThreeService
}

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

Так что теперь вы не можете получить что-то вроде:

switch(...) {
   case ...:
       ...
   default:
       throw new IllegalArgumentException("Unknown type: " + enum.name());
}

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

0 голосов
/ 10 июня 2018

Скажем, у вас есть домен (ваши домашние животные), где вы знаете, что существует определенное перечисление (количество) типов.Например, у вас есть два и только два питомца (которых вы будете моделировать с классом MyPet).Meowsi - ваша кошка, а Fido - ваша собака.

Сравните следующие две реализации этого надуманного примера:

sealed class MyPet
class Meowsi : MyPet()
class Fido : MyPet()

Поскольку вы использовали запечатанные классы, когда вам нужно выполнить действие в зависимости отЧто касается типа питомца, то возможности MyPet исчерпаны в двух, и вы можете убедиться, что экземпляр MyPet будет точно одним из двух вариантов:

fun feed(myPet: MyPet) {
    when(myPet) {
       is Meowsi -> print("Giving cat food to Meowsi!")
       is Fido -> print("Giving dog biscuit to Fido!") 
    }
}

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

open class MyPet
class Meowsi : MyPet()
class Fido : MyPet()

fun feed(myPet: MyPet) {
    when(myPet) {
       is Mewosi -> print("Giving cat food to Meowsi!")
       is Fido -> print("Giving dog biscuit to Fido!) 
       else -> print("Giving food to someone else!") //else statement required or compiler error here
    }
}

Другими словами, без запечатанных классов не будет исчерпания (полного охвата) возможности.

Обратите внимание, что вы можете достичь исчерпания возможностей с Java enum, однако это не полноценные классы.Например, enum не может быть подклассом другого класса, только реализовать интерфейс (спасибо EpicPandaForce ).

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

Без sealed class, кто-то еще в вашем доме / приложении может определить новый MyPet:

class TweetiePie : MyPet() //a bird

И этот нежелательный питомец будет кормиться вашим методом feed, поскольку он включен в оператор else:

       else -> print("Giving food to someone else!") //feeds any other subclass of MyPet including TweetiePie!

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

Отсюда необходимость sealed классов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...