Неэффективность защитной копии в Java

Я давний программист C/C++, который изучает Java. Я читал о проблеме нарушения инкапсуляции с помощью метода доступа, который возвращает ссылку на приватное поле. Стандартное решение Java кажется защитным копированием - вызывая конструктор копирования или clone() для создания копии поля и возвращая ссылку на копию. Я не понимаю, почему никто не обеспокоен неэффективностью создания защитной копии. В C++ средство доступа просто возвращает указатель на const, защищая закрытый член без копирования. Почему в Java нет ссылки на const?

2 ответа

Почему в Java нет ссылки на const?

На вопросы могут только правильно ответить разработчики языка, но я думаю, что проблема заключалась в том, что они не могли понять, как заставить это работать как часть языкового дизайна. Мое воспоминание (из некоторого документа "Обоснование дизайна Java", с которым я сталкивался однажды) было то, что Гослинг и его коллеги изначально хотели поддержать const...

На самом деле, хотя и C и C++ оба поддерживают const как способ выражения ограничения изменчивости, они оба также имеют лазейки, которые позволяют некоторому коду "нарушать" ограничение. (См. Статью в Википедии о правильности констант.) Возможно, именно из-за сложностей с разработкой Java не было (или не нужно) таких лазеек, что заставило Гослинга и его коллег отказаться от этой идеи.

Обратной стороной является то, что необходимость защищенного копирования в Java не так велика, как вы можете себе представить, и что затраты на это не так велики, как вы могли бы себе представить. И когда стоимость защитной копии значительна, в Java есть и другие варианты... например, создание "неизменяемых" объектов-оболочек или интерфейсов, которые поддерживают только операции "чтения".

ПРИМЕЧАНИЕ: я не знаю, что вы найдете прямой ответ на вопрос, почему нет const на Яве. (Я предполагаю, что из-за динамической природы Java наличие ключевого слова const не обеспечивало столько оптимизаций компилятора и, следовательно, считалось, что его не стоит вкладывать - но это только мое мнение. [В C++ у вас есть все окончательные конкретные типы доступны для вас во время компиляции, на Java вы этого не сделаете.])

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

Некоторые распространенные типы (String, Date) являются неизменяемыми и предназначены для передачи и получения от получателей с минимальными накладными расходами, не допуская изменений.

Как указывает @DavidWallace, существуют методы, которые создают поверхностную, неизменяемую копию карты и позволяют вернуть ее вызывающей стороне - чтобы гарантировать, что они не будут связываться с ней. Это практическое решение, однако оно не применяет его во время компиляции.

Если мы говорим о картах: java.util.Map, согласно контракту интерфейса, изменчив. Чтобы достичь чего-то близкого к const, вы можете легко создать другой, но простой интерфейс только с помощью метода поиска:

public interface Lookup<K,V> {
    public V get(K);
}

И вернуть экземпляр этого. Таким образом, гарантируя, что никто не изменит его... во время компиляции... потому что такого метода не существует.

А реализация MapLookup, которая реализует вышеизложенное путем обертывания карты без создания копии, похожа на 5 строк кода.

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

Другие вопросы по тегам