Когда используется экземпляр правильного решения? - PullRequest
11 голосов
/ 22 января 2012

Как я всегда это понимал, основные случаи, когда instanceof подходит:

  1. Реализация Object.equals(Object).Поэтому, если бы я писал класс List, а по какой-то причине не расширял AbstractList, я бы реализовал equals(o), сначала протестировав o instanceof List, а затем сравнив элементы.) оптимизация для особого случая, когда не меняет семантику, а только производительность.Например, Collections.binarySearch выполняет тест instanceof RandomAccess и использует несколько иной бинарный поиск для RandomAccess и не- RandomAccess списков.

Я не думаю, что instanceof представляетзапах кода в этих двух случаях.Но есть ли другие случаи, когда целесообразно использовать instanceof?

Ответы [ 4 ]

6 голосов
/ 27 августа 2013

Один из способов ответить на ваш вопрос - ответить «когда в твердой библиотеке Java используется instanceof?» Если мы предположим, что Guava является примером хорошо спроектированной библиотеки Java, мы можем посмотреть, где она использует instanceof, чтобы решить, когда это допустимо.

Если мы извлечем jav-файл с исходным кодом Guava и выполним grep, мы увидим, что instanceof упоминается 439 раз в 122 файлах:

$ pwd
/tmp/guava-13.0.1-sources
$ grep -R 'instanceof' | wc -l
439
$ grep -Rl 'instanceof' | wc -l
122

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

  • Для проверки на равенство

    Это наиболее распространенное использование. Это в некоторой степени зависит от реализации, но если вы действительно хотите измерить равенство на основе класса / интерфейса, который он расширяет / реализует, вы можете использовать instanceof, чтобы убедиться, что объект, с которым вы работаете. Однако это может вызвать странные проблемы, если дочерний класс переопределяет equals() и не соответствует тем же требованиям instanceof, что и родительский. Примеры везде, но простой - это Lists.equalsImpl(), который используется ImmutableList.

  • Для короткого замыкания ненужного строительства объекта

    Вы можете использовать instanceof, чтобы проверить, можно ли безопасно использовать передаваемый аргумент или вернуть его без дальнейшего преобразования, например, если это уже экземпляр нужного класса или мы знаем, что он неизменный. См. Примеры в CharMatcher.forPredicate(), Suppliers.memoize(), ImmutableList.copyOf() и т. Д.

  • Доступ к деталям реализации без раскрытия другого поведения

    Это можно увидеть повсюду в Гуаве, но особенно в статических служебных классах в пакете com.google.common.collect, например, в Iterables.size(), где он вызывает Collection.size(), если это возможно, и в противном случае подсчитывает количество элементов в итераторе за O(n) раз.

  • Чтобы не звонить toString()

    Я скептически отношусь к тому, что это делается более чем в нескольких отдельных случаях, но, если вы уверены, что делаете все правильно, вы можете избежать ненужной конвертации CharSequence объектов в String с помощью instanceof, как в Joiner.toString(Object).

  • Выполнить сложную обработку исключений

    Очевидно, что «правильное» решение - использовать блок try/catch (хотя на самом деле он уже делает instanceof проверок), но иногда у вас есть более сложная логика обработки, которая заслуживает использования условных блоков или передачи обработки на отдельный метод, например, устранение причин или обработка, зависящая от реализации. Пример можно найти в SimpleTimeLimiter.throwCause().

Одна вещь, которая обращает на себя внимание при таком поведении, это то, что почти все они решают проблемы Я не должен решать . Они полезны в коде библиотеки, например в Iterables, но если я реализую это поведение, я, вероятно, должен спросить себя, нет ли библиотек или утилит, которые решают эту проблему для меня.

Во всех случаях я бы сказал, что instanceof проверки должны когда-либо использоваться только внутренне в качестве детали реализации - то есть вызывающий объект любого метода, который опирается на instanceof проверку, не должен быть в состоянии (легко ) расскажи что ты сделал. Например, ImmutableList.copyOf() всегда возвращает ImmutableList. В качестве детали реализации он использует instanceof, чтобы избежать создания новых ImmutableList s, но это необязательно для приемлемого обеспечения ожидаемого поведения.

Кроме того, было забавно встретить ваше имя, Луи, когда я копался в исходном коде Гуавы. Клянусь, я понятия не имел!

6 голосов
/ 22 января 2012

Устаревший код или API-интерфейсы, находящиеся вне вашего контроля, являются законным вариантом использования instanceof. (Даже тогда я предпочел бы написать слой OO поверх него, но время от времени иногда исключает такой редизайн.)

В частности, фабрики, основанные на иерархиях внешних классов, кажутся широко используемыми.

4 голосов
/ 22 января 2012

Ваш первый случай - это пример, в котором я не буду использовать оператор instanceof, но посмотрим, равны ли классы:

o != null && o.getClass() == this.getClass()

Это позволит избежать экземпляра A extends B и * 1006.* считаются равными

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

  • фабричные экземпляры, где у вас есть, например, canCreate и create метод, который получает общий интерфейс в качестве параметра.Каждая из фабрик может обрабатывать определенную реализацию интерфейса, поэтому для этого потребуется instanceof.Определение только интерфейса в фабричном абстрактном классе / интерфейсе позволяет, например, написать составную фабрику
  • составные реализации (как показано в моем первом примере)
0 голосов
/ 22 января 2012

Как вы упомянули, "правильное" использование instanceof довольно ограничено.Насколько я знаю, вы в основном суммировали два основных использования.

Однако вы можете немного обобщить свои утверждения, как показано ниже:

  1. Проверка типов перед необходимыми приведениями.
  2. Реализация особых сценариев, зависящих от очень конкретных экземпляров классов
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...