Замена например Java?

10

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

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

Как лучше всего обходить этот способ? Я прочитал некоторые вещи о полиморфизме, но я не могу найти примеры, которые решают проблему, которую я имею. Все они обычно переопределяют метод, который для меня не работает, так как мне нужно вытащить разные вещи из разных объектов.

Есть ли лучший способ сделать это, или я застрял с использованием instanceofили какое-то отражение, чтобы получить поля, специфичные для объектов?

Томас Митчелл
источник
10
Дело не в том, что ключевое слово instanceofневерно; попытка найти класс объекта обычно является проблемой. Не всегда неправильно, но, вероятно, в вашем случае это так. Возможно, если вы расскажете нам, чего пытаетесь достичь, мы можем предложить решение с использованием полиморфизма.
Андрес Ф.
2
Хорошо, у каждого класса есть данные, специфичные для него. Я хочу получить конкретные данные из них. Я думаю, что понял ответ с помощью, которую я имел. Что-то вроде вызываемого метода, getSpecifics()который реализуется по-разному для каждого, каждый из которых возвращает данные, специфичные для класса?
Томас Митчелл

Ответы:

16

Причиной instanceofразочарования является то, что это не ООП.

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

Если вам нужно другое поведение в подклассах, добавьте метод и реализуйте их по-разному.

чокнутый урод
источник
1
Таким образом, метод getSpecifics()(или что-то подобное) для каждого класса, который будет возвращать специфику для каждого класса. Это лучший подход?
Томас Митчелл
@Schmooo Вам действительно нужно подумать о том, где общая функциональность существует в дереве классов и какие контракты существуют на каждом уровне.
3
Но как насчет случая, когда ваш объект пересекает слои и информация о типе теряется? Пример я создаю List. Я передаю это объекту, который берет любой Iterable. Теперь этот второй объект передает его третьему объекту, который принимает либо Listдля оптимизации, либо Iterableгораздо медленнее. Второй не должен знать, что это список, но третий очень хотел бы знать. Разве третий объект не должен проверять, например, instanceof, может ли он применить оптимизацию? Посмотрите, например, гуаву, FluentIterableкоторая делает именно это.
Лоран Бурго-Рой
1
Я бы сказал, что это не всегда тот случай, когда вам нужно добавить метод в класс. Например, некоторый код получает исключение и должен что-то делать с ним в зависимости от его типа. Скажем, делает отчет или что-то восстанавливает после ошибки. Такая функциональность определенно не подходит для исключений. Или, если классы взяты из какой-то внешней библиотеки, у вас даже нет средств изменить эти классы. В этом случае я думаю, что абсолютно справедливо полагаться на instanceof.
Малкольм,
9

instanceof это не обязательно плохо, но на это нужно смотреть.

Пример, где он работает правильно, находится в месте, где вы получаете коллекцию базового типа, а вам нужны только подтипы. Получение сетевых адресов NetworkInterface.getNetworkInterfaces()возвращает объекты NetworkInterface, которые имеют коллекцию объектов InetAddress, некоторые из которых - Inet4Address, а некоторые - Inet6Address. Если кто-то хочет отфильтровать коллекцию для объектов Inet4Address, необходимо использовать instanceof.

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

Когда вы возвращаете базовый класс, если для этого нет веской причины (обратная совместимость между спецификациями более ранней версии), вы не должны пытаться заглянуть в базовые типы. Если вам возвращают набор, вы знаете, что получаете набор. Позже разработчик может изменить свое мнение, чтобы вернуть более конкретный тип (SortedSet) или изменить базовый тип (HashSet на TreeSet), не нарушая ничего.

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


источник
Вполне возможно, что наличие интерфейса, определяющего isOfType(SomeEnum.IPv4)метод, может быть лучшим способом фильтрации таких качеств, чем проверка конкретного типа с помощью instanceof. Что если вы захотите разделить ваш класс реализации IPv4 позже? Не то чтобы это всегда лучше, но это соображение.
Стивен Шланскер
@StevenSchlansker Это может работать для этого конкретного класса. Но что, если нужно проверить конкретный тип, чтобы увидеть, возможно ли приведение (или можно просто привести и отловить исключение?) Inet6Address реализует больше методов, чем InetAddress. Для него было бы трудно работать с интерфейсами (перечисление, которое перечисляет каждый класс в пакете? Или загрузчик классов?), И это означало бы, что метод должен быть реализован вручную (или некоторый код отражения в Object). Подумайте, как это сделать (o instanceof Serializable) || (o instanceof Externalizable). instanceof лучше альтернативы
1

Вы можете использовать метод getClass ().

Вы уверены, что вам нужно три разных класса? Может быть, один класс с переключателем внутри будет лучше служить?

Гангнус
источник
7
getClassи instanceofподелиться минусами. Полиморфизм лучше, чем оба, когда он подходит, и я не вижу, чтобы он не соответствовал сценарию использования OP.
Я ничего не имею против вашего первого предложения. Но второй - извините, я не могу понять, что вы имеете в виду вообще.
Гангнус
1
Разве getClassя не разделяю ту же проблему, что и при использовании instanceof? Мне все еще придется выяснить, что у меня есть, а затем вызвать набор функций. В идеале мне нужен метод, который возвращает данные, специфичные для этого класса, без необходимости приведения к этому объекту.
Томас Митчелл
Я жалуюсь на то, что вы игнорируете полиморфизм, так как это часто лучший выбор. Я уверен, что вариант использования в вопросе может быть сделан с полиморфизмом, поэтому я не вижу необходимости использовать также. (Кроме того, просто воспроизводить чужой ответ
2
@Gangus Я не считаю это «атакой», я имею в виду без обид. Но что угодно. Нет, вопрос не в том, «что еще можно использовать», а в том, «есть ли лучший способ сделать это». Если вы не хотите спорить getClass, это лучший способ сделать это, он не имеет никакого дела с принятием большей части ответа ;-)
1

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

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

Тем не менее, instanceof обычно лучше, чем сравнение классов, поскольку он правильно поддерживает наследование. Вы также можете использовать такие методы, как isAssignableFrom и другие из API рефлексии. Если вы хотите реализовать что-то вроде динамической диспетчеризации, это можно сделать с помощью API отражения, но будьте осторожны, это будет медленно. Используйте с осторожностью, в идеале вы должны исправить структуру объекта и дизайн вашего приложения, если можете.

Надеюсь это поможет

Newtopian
источник