Есть ли язык программирования, где вы можете запретить приведение классов к возвращаемым типам? - PullRequest
2 голосов
/ 13 февраля 2020

Фон

Возьмем для примера этот простой класс java:

class SomeWrapper {

    List<SomeType> internalDataStructure = new ArrayList<>();
    boolean hasSomeSpecificSuperImportantElement = false;
    // additional fields that do useful things

    public void add(SomeType element) {
        internalDataStructure.add(element);
        if (SUPER_IMPORTANT.equals(element)) {
            hasSomeSpecificSuperImportantElement = true;
        }
    }

    public Iterable<SomeType> getContents() {
        return internalDataStructure;
    }

    // additional methods that are definitely useful

}

Хотя он выглядит несколько хорошо, у него есть большой недостаток. Поскольку internalDataStructure возвращается как есть, вызывающие функции getContents () могут классом привести его к List и изменить внутреннюю структуру данных так, как это не предполагается.

class StupidUserCode {

    void doSomethingStupid(SomeWrapper arg) {
        Iterable<SomeType> contents = arg.getContents();

        // this is perfectly fine
        List<SomeType> asList = (List<SomeType>) contents;

        asList.add(SUPER_IMPORTANT);
    }

}

В java и других аналогичных В языках программирования эту проблему можно решить, обернув внутреннюю структуру данных в какую-то неизменяемую оболочку. Например:

    public Iterable<SomeType> getContents() {
        return Collections.unmodifiableList(internalDataStructure);
    }

Это работает сейчас, но, по моему скромному мнению, имеет ряд недостатков:

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

Вопрос

Существуют ли какие-либо языки программирования, в которых вы можете указать тип возвращаемого значения способ быть невозможным быть классом?

Я думал о чем-то вроде syntheti c типов, где добавлялся восклицательный знак! до конца имя типа делает его не-классно-способным. Как это:

    public Iterable!<SomeType> getContents() {
        return internalDataStructure;
    }

    void doSomethingStupid(SomeWrapper arg) {
        // The ! here is necessary because Iterable is a different type than Iterable!
        Iterable!<SomeType> contents = arg.getContents();

        // this now becomes a compile-time error because Iterable! can not be cast to anything
        List<SomeType> asList = (List<SomeType>) contents;

        asList.add(SUPER_IMPORTANT);
    }

1 Ответ

2 голосов
/ 13 февраля 2020

Поскольку речь идет об изменчивости, Java пример List против Iterable не является хорошим примером изменяемых и неизменяемых типов данных (метод Iterator.remove мутирует базовую коллекцию, поэтому List может быть поврежден внешним вызывающим абонентом даже без приведения).

Давайте вместо этого представим два типа: MutableList и ReadonlyList, где MutableList - это подтип, и только ReadonlyList предотвращает мутирование пользователем; сам список не гарантируется, чтобы избежать мутации. (Мы не можем разумно назвать супертип ImmutableList, потому что никакое значение не является одновременно изменяемым и неизменным списком.)


Преобразование из супертипа в подтип, например, с ReadonlyList до MutableList, называется downcasting . Даункастинг небезопасен, потому что не каждое значение супертипа является значением подтипа; так что либо проверка должна выполняться во время выполнения (как это делает Java, бросая ClassCastException, если вызываемый экземпляр не имеет правильного типа), либо программа сделает что-то небезопасное для памяти (как может сделать C) ).

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

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

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

...