Xpoint
   [напомнить пароль]

Элементы Vector разного типа

Метки: [без меток]
[арх]
2006-04-24 22:04:11 [обр] Ilya Rudomilov[досье]
Vector telephoneTalk = new Vector();
telephoneTalk.add(new TelephoneTalk("2006-05-01","10 min"));
telephoneTalk.add(new TelephoneTalkCity("2006-05-02","5 min","MSK"));
telephoneTalk.add(new TelephoneTalkCountry("2006-05-02","20 min","USA"));

TelephoneTalkCity и TelephoneTalkCountry - подклассы класса TelephoneTalk.
Произвожу обработку telephoneTalk итератором. Надо определить, какого типа элемент в векторе. instanceof не подходит, т.к. в итераторе:

TelephoneTalk tt = (TelephoneTalk) it.next();

Если преобразование типа из правой части убрать, то вообще не компилируется, типа несовместимые типы. По instanceof все получаемые элементы вектора - TelephoneTalk.

PS Извиняюсь за глупые вопросы - я просто учусь. Буду очень благодарен ответу.

спустя 12 часов [обр] Данбала(2/63)[досье]
Не важно, что вы привели типы. instanceof для TelephoneTalkCity будет отличаться от instanceof TelephoneTalkCountry. Важно, что после new.
спустя 5 часов [обр] Владимир Палант(253/4445)[досье]
Object o = it.next();
if (o instanceof TelephoneTalk) {
  TelephoneTalk tt = (TelephoneTalk)o;
  ...
}
Но не следует хранить в Vector данные, для которых лучше бы создать собственный объект. Если у вас телефонные разговоры привязаны к городу/стране, то лучше определить объект, в котором эти данные будут храниться — и не заморачиваться с проверками типов и порядком следования объектов в списке.
спустя 1 час 33 минуты [обр] Ilya Rudomilov[досье]

Данбала: пробовал я. К примеру, элементарнейший нижеописанный код выдает везде "TelephoneTalk", хотя в векторе telephoneTalk есть как элементы TelephoneTalkCity, так и элементы TelephoneTalkCountry.

      it = telephoneTalk.iterator();
      TelephoneTalk tt;
      while (it.hasNext()){
         tt = (TelephoneTalk) it.next();
         
         if (tt instanceof TelephoneTalk) {
            JOptionPane.showMessageDialog(null, "TelephoneTalk");
         }
         else if (tt instanceof TelephoneTalkCity) {
            JOptionPane.showMessageDialog(null, "TelephoneTalkCity");
         }
         else {
            JOptionPane.showMessageDialog(null, "TelephoneTalkCountry");
         }
         
         
      }
Но не следует хранить в Vector данные, для которых лучше бы создать собственный объект. Если у вас телефонные разговоры привязаны к городу/стране, то лучше определить объект, в котором эти данные будут храниться — и не заморачиваться с проверками типов и порядком следования объектов в списке.

Структуру придумывал не я, посему работаю с тем, что есть. Если бы сам планировал организацию, то, наверное, так бы и сделал.
Указанный вами способ не работает. Проверял кодом:

      it = telephoneTalk.iterator();
      
      while (it.hasNext()){
         Object tt = it.next();
         
         if (tt instanceof TelephoneTalk) {
            JOptionPane.showMessageDialog(null, "TelephoneTalk");
         }
         else if (tt instanceof TelephoneTalkCity) {
            JOptionPane.showMessageDialog(null, "TelephoneTalkCity");
         }
         else {
            JOptionPane.showMessageDialog(null, "TelephoneTalkCountry");
         }
         
         
      }
спустя 15 минут [обр] Данбала(2/63)[досье]

Ilya Rudomilov[досье]
Правильно, потому что instanceof TelephoneTalk как для TelephoneTalkCity, так и TelephoneTalkCountry будет true, т. к. являются его производными. Надо так

if (tt instanceof TelephoneTalkCity) {
    JOptionPane.showMessageDialog(null, "TelephoneTalkCity");
} else
if (tt instanceof TelephoneTalkCountry) {
    JOptionPane.showMessageDialog(null, "TelephoneTalkCountry");
} else {
    JOptionPane.showMessageDialog(null, "TelephoneTalk");
}

если ни City, ни Country не являются подклассами кого либо из друг друга.

спустя 20 минут [обр] Владимир Палант(253/4445)[досье]
Ilya Rudomilov[досье]
Не заметил, что TelephoneTalkCounty — подкласс TelephoneTalk. Тогда объект класса TelephoneTalkCounty является одновременно объектом класса TelephoneTalk и см. ответ Данбала[досье] выше. Ну или, если очень приспичит (не рекомендую) — obj.getClass().getName().
спустя 2 часа 29 минут [обр] Данбала(2/63)[досье]

Владимир Палант[досье]

Ну или, если очень приспичит...

Пытался придумать, как извратить эту задачу, чтобы так приспичило, ничего не вышло...
Ilya Rudomilov[досье]
Получайте из Vector-а интерфейс и не парьтесь.

спустя 22 часа [обр] Ilya Rudomilov[досье]
Не заметил, что TelephoneTalkCounty — подкласс TelephoneTalk. Тогда объект класса TelephoneTalkCounty является одновременно объектом класса TelephoneTalk и см. ответ Данбала выше. Ну или, если очень приспичит (не рекомендую) — obj.getClass().getName().
Огромное спасибо. :) У меня идея насчет getClass() была, но руки никак не доходили попробовать. Всем спасибо за советы, вопрос исчерпан. :)
спустя 14 часов [обр] Данбала(2/63)[досье]

Владимир Палант[досье]

У меня идея насчет getClass() была, но руки никак не доходили попробовать.

Теперь они у него дошли. Кого бы Вы за это поблагодарили?

спустя 1 час [обр] 30-ый(59/584)[досье]

Данбала[досье], поддерживаю. Стыдно, товарищ Палант!

Ilya Rudomilov[досье]: Немедлено забудьте про getClass().getName(). В этой задаче вы должны пользоваться instanceof и только instanceof.

getClass().getName() следует применять только в том случае, когда имя класса на этапе компиляции не известно.

спустя 4 часа 52 минуты [обр] Владимир Палант(253/4445)[досье]
Ну, я же написал — не рекомендую. Это решение только для самого крайнего случая. А здесь пока что не было названо никаких причин, почему нельзя использовать instanceof, который в 100 раз предпочтительней.
спустя 1 день 8 часов [обр] Данбала(2/63)[досье]
Владимир Палант[досье]
Отмазка.
спустя 7 дней [обр] Даниэль Алиевский(35/125)[досье]

Ilya Rudomilov[досье]
Между прочим, все это радикально противоречит элементарным правилам хорошего тона ООП. Вы ведь хотите напечатать некое "имя" класса - второй параметр выших showMessageDialog? Так сделайте метод name() в TelephoneTalk, который возвращает требуемое имя. И перекройте его в наследниках.

Простое правило. В хорошо написанной Java-программе вызовы instanceof или (тем более) getClass() не должны встречаться почти никогда. (Как goto в Паскале или Си.) К проверке класса допустимо прибегать, только если все прочие пути исчерпаны. Это бывает нужно лишь в глубоко системных случаях, вроде расширения системы заранее неизвестным набором пакетов. Обычно же класс прекрасно может сам "рассказать" о себе все, что вы пожелаете узнать, вроде своего названия - просто снабдите его соответствующими методами.

Более того, всякое ветвление, содержащее более 2-3 вариантов - или, боже упаси, потенциально расширяемое до неопределенного количества случаев, как у вас - это признак плохого программирования. Нормой является использование нескольких реализаций базового интерфейса и обращение к требуемой реализации. Как в вашем случае: ветвление вообще не нужно, нужен метод name(), реализованный различными способами.

Почитайте книжки по шаблонам проектирования и идиомам Java.

спустя 1 день 11 часов [обр] 30-ый(59/584)[досье]
В хорошо написанной Java-программе вызовы instanceof

Может это и в самом деле не самый лучший оператор, что чтобы "почти никогда"... это пожалуй ерунда.

Бывает очень много маленьких простых подзадач, где описываемое в теме решение очень хорошо решается при помощи "instance of". В частности, когда у вас клиентское приложение получает данные (например от EJB или по SOAP) в коллекции неких "Value Object", которые в себе вообще никакой логики не содержат и содержать не должны(!). Делать для них в каждом случае какую-нибудь обертку - это часто слишком большая цена за элегантность.

спустя 1 день 20 часов [обр] Даниэль Алиевский(35/125)[досье]
Когда приложение получает данные откуда-то "извне" - это, пожалуй, и есть тот самый "глубоко системный случай", где может быть уместным не только instanceof, но даже рефлексия. 99% прикладных классов, решающих конечные задачи (от UI до алгоритмики), не нуждаются в подобных вызовах.
спустя 8 минут [обр] Данбала(2/63)[досье]
Даниэль Алиевский[досье]
Marker interface pattern — тоже глубоко системный случай?
спустя 3 часа 19 минут [обр] 30-ый(59/584)[досье]

Ну что такое "Marker interface pattern" я пожалуй не знаю, но с уверенностью скажу, что в классической MVC архитектуре очень часто требуется использование instanceof при взаимодействии, например, модели и контроллера. Ибо ту логику, которая должна быть реализована в контроллере было бы совершенно нелепо запихивать в объекты модели и наоборот.

При этом, заметьте, отдельные элементы MVC остаются таки одним приложением, никакого "извне" нет, и контроллеру доступны все публичные сингатуры модели. Рефлексия здесь была бы бредом, а instanceof - как раз самое то, что надо!

спустя 29 минут [обр] Данбала(2/63)[досье]
30-ый[досье]
Clonable, Serializable, Externalizable + те интерфейсы из внешних библиотек, которые вы используете (посмотрите, Avalon, например). Вообще-то, это азбука. Простите.
спустя 6 минут [обр] Данбала(2/63)[досье]
30-ый[досье]
И еще. Даже не приплетая MVC, можно придумать много задач, вовсе глубоко не системных, где применение instanseof — единственно верное решение. Посмотрите на Swing, точнее на любой его observer.
спустя 10 часов [обр] Даниэль Алиевский(35/125)[досье]

Уважаемые, вы забываете, что данный тред создан программистом, чей уровень, очевидно, заметно уступает вашему. Более того, среди потенциальных читателей этого форума большинство, вероятно - непрофессионалы. Так чему же вы учите новичков?

В вопросе, первоначально заданном автором, instanceof, безусловно, совершенно не в тему. Правильное решение - метод name() в TelephoneTalk. И в подавляющем большинстве кода, который создается каждый день не только новичками, но и профессионалами, instanceof совершенно не нужен. Неужели будете спорить?

Простое сравнение. Очень многие уважаемые библиотеки, даже на Java, используют ассемблерное программирование и системные вызовы. Действительно, иногда это позволяет резко увеличить эффективность, а иногда является единственным путем решения задачи. Но это никак не противоречит тому факту, что применение ассемблера в 99% повседневных ситуаций совершенно не нужно и является скорее плохим тоном.

Да, действительно, в Swing (подкаталог javax/swing из исходников jdk1.5.0_06) instanceof встречается часто. У меня получилось 1619 вхождений в 231 файле, при этом там всего 607 java-файлов. Но ведь Swing и есть тот самый "глубоко системный случай". В обычной практике очень редко приходится создавать библиотеки такого класса. И даже в Swing, как я вижу по исходникам, очень часто instanceof служит лишь для обеспечения совместимости с более ранним AWT. Например, проверяется, что элемент - экземпляр JComponent, поскольку для более раннего Component нужной функциональности еще не было.

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

Я не спорю, что иногда instanceof нужен. Например, для оптимизации - может быть, некая известная мне реализация интерфейса предусматривает более эффективный способ сделать некое действие, чем универсальное решение. (Пример: мне нужен многократный произвольный доступ к элементам списка, а на входе я получаю List, который с большой вероятностью может оказаться ArrayList.) Или для совместимости со старым либо чужим кодом, когда я уже не могу внести исправления в некий класс и должен обработать его особым образом. Но все это, как правило, исключения либо компромиссы. При написании нового, "чистового" пакета, не отягощенного совместимостью со старым кодом, обычно можно почти не употреблять instanceof. При проектировании пакета с чистого листа практически всегда можно заменить проверку instanceof вызовом некоторого метода предка, который сообщит более адекватную информацию, чем просто факт принадлежности экземпляра к какой-то ветке наследования.

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

спустя 11 минут [обр] Даниэль Алиевский(35/125)[досье]
(Конечно, Cloneable, а не Clonable)
спустя 21 минуту [обр] 30-ый(59/584)[досье]

А в ваших словах действительно что-то есть.

  1. Описанный в первой реплике пример (иногда полезно перечитать начало темы :-) действительно лучше переписать через абстрактный метод getName() в корне дерева.
  2. Только что пересмотрел 5 мегабайтный EJB-проект. Действительно instance of встречается там достаточно редко.. что-то около 30 раз. При этом:

 - половина - это проверка в методе equals() различных объектов.
 - еще четверть - универсальные методы по трансформации объектов в XML.
 - оставшаяся четверть - сортировка Value-Objects, которые кучей приходят в коллекции.

спустя 11 минут [обр] Даниэль Алиевский(35/125)[досье]

Вспоминал ведь, но потом забыл :) Конечно, instanceof совершенно уместен и даже рекомендован в реализации equals(). Там даже проверка класса (getClass()) вполне уместна, если по замыслу наследник не может быть равен предку.

А зачем понадобились instanceof для трансформации в XML или сортировки? Просто интересно.

действительно лучше переписать через абстрактный метод getName() в корне дерева.

Или name() Тут вроде бы нет строгих правил, но я предпочитаю использовать префикс get, только если есть (или по крайней мере потенциально возможен) обратный метод set.

спустя 29 минут [обр] 30-ый(59/584)[досье]

Трансформация (существенно упрощенный пример!):
 - Нужен метод, который конвертит коллекции объектов.
 - Коллекции одинаковые, типа <set><items><i/><i/>...<i/><items><refs></refs></set>.
 - Объекты могут быть простые, без подчиненных. А могут быть сложные, тогда в <refs> будут добавлены связанные объекты... например производитель для машины или покупатель для счета-фактуры.

Соответвенно, чтобы отличать одни объекты от других я проверяю реализацию ими интерфейса XmlRefs с методом getReferences() и при необходимости создаю ветку <refs/>.

Сортировка:
От клиента приходит коллекция цен для некого товара. Там есть самые разные цены: отпускная, закупочная, рекомендованная (UVP), залоговая (Pfand) и т.д. и т.п. Каждая цена это маленький объект. Все цены хоть являются наследниками объекта AbstractPreis, имеют часто множество дополнительных свойств.

Исторически сложилось, что цены сохраняются в разных таблицах и часто очень по-разному, например как нетто (т.е. без НДС) или как брутто, с привязкой к товару или к производителю и т.д.

Объекты надо как-то друг от друга отличать? Это и происходит через instance of. Появление еще одного типа цен тут боятся не стоит, ибо если это произойдет, по-любому огромный блок переписывать придется.

спустя 19 минут [обр] Даниэль Алиевский(35/125)[досье]
Соответвенно, чтобы отличать одни объекты от других я проверяю реализацию ими интерфейса XmlRefs с методом getReferences() и при необходимости создаю ветку <refs/>.

Если вы изначально знали, что объекты иногда имеют связанные с ними объекты, разве не логичнее было в базовых класс заложить метод getReferences(), возвращающий пустой массив (или коллекцию), если связанных объектов нет? Понятно, у меня мало информации, но на первый взгляд (из вашего текста) все это выглядит, как если бы в DOM для представления тегов, имеющих дочерние теги, использовался отдельный интерфейс с методом вроде getChildren...

Объекты надо как-то друг от друга отличать? Это и происходит через instance of. Появление еще одного типа цен тут боятся не стоит, ибо если это произойдет, по-любому огромный блок переписывать придется.

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

Совсем непонятно, при чем тут сортировка :)

спустя 28 минут [обр] 30-ый(59/584)[досье]
  1. Объектов, которые имею связи - 5. Которые нет - 50. Таскать с собой везде нелепый getReferences(), который кроме исключения NotSupported ничего не делает - идейно неверно. Для этого IMHO служат интерфейсы. Кроме этого есть в коде другие различия в обработке этих двух типов классов. Полагаться на пустой объект или уж тем более на исключение NotSupported было бы совсем нехорошо.
  1. "Сортировка" в смысле отличия друг от друга. Когда вы сортируете например посуду, вы же просто делите ее на группы - чашки, тарелки, вилки - не создавая при этом строгой последовательности элементов :-)
  1. Простого и легкого добавления цены здесь не будет. К каждой цене приложена сложная и в своем роде уникальная логика. Таким образом с точки зрения Backend'а это очень разные объекты. Во Frontend'е все цены напротив почти одинаковые и редактируются в одном и том же интерфейсе. В данном примере мы имеем Frontend-ориентированную структуру объектов, которая должна приводится к Backend-ориентированной через цепочку сложных преобразований. Бывает и обратные примеры... тогда instance of будут в клиентской части...
спустя 5 часов [обр] Данбала(2/63)[досье]

Даниэль Алиевский[досье]

99% прикладных классов, решающих конечные задачи (от UI до алгоритмики), не нуждаются в подобных вызовах.

Давайте так. Существует класс задач, в котором самым оптимальным будут решения с использованием instanceof. Также существует другой класс задач, для которого это неверно. Понятно, что использование решений из первого класса для второго глупо. Но, с другой стороны, то, что первый класс значительно уже второго, не означает, что от использования instnaceof нужно отказываться везде, где бы то ни было. Иначе мы получим неэффективные решения для задач первого класса. Согласны?

В вопросе, первоначально заданном автором, instanceof, безусловно, совершенно не в тему.

Даже безусловно?
Цитата автора:

Структуру придумывал не я, посему работаю с тем, что есть.

Отсюда можно сделать вывод, что автор откуда-то получает уже готовые, кем-то написанные объекты, например, по идентификатору, и единственное, что он может с ними сделать — это вызывать их методы. Но как он может их вызвать, если он не знает, какими методами обладает полученный объект? Однако, эту информацию он может получить исходя из типа объекта. Например, если instanceof TelephoneTalkCountry есть true, то у объекта есть метод getCountry(). Не так уж безусловно, не находите?

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

Бред. Причем категоричный, неаргументированный и совершенно безосновательный. Поэтому без комментариев.

спустя 1 час 52 минуты [обр] Даниэль Алиевский(35/125)[досье]
Давайте так. Существует класс задач, в котором самым оптимальным будут решения с использованием instanceof. Также существует другой класс задач, для которого это неверно. Понятно, что использование решений из первого класса для второго глупо. Но, с другой стороны, то, что первый класс значительно уже второго, не означает, что от использования instnaceof нужно отказываться везде, где бы то ни было. Иначе мы получим неэффективные решения для задач первого класса. Согласны?

Согласен. Я говорил: "К проверке класса допустимо прибегать, только если все прочие пути исчерпаны."

Отсюда можно сделать вывод, что автор откуда-то получает уже готовые, кем-то написанные объекты, например, по идентификатору, и единственное, что он может с ними сделать — это вызывать их методы. Но как он может их вызвать, если он не знает, какими методами обладает полученный объект? Однако, эту информацию он может получить исходя из типа объекта. Например, если instanceof TelephoneTalkCountry есть true, то у объекта есть метод getCountry(). Не так уж безусловно, не находите?

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

Бред. Причем категоричный, неаргументированный и совершенно безосновательный. Поэтому без комментариев.

Собственно, этот бред я извлек из книги Джошуа Блоха "Effective Java" ("Java - Эффективное программирование"), статья 10. Его аргументы мне показались вполне достаточными, и я разлеляю его точку зрения. Зачем использовать интерфейс без методов для отметки, что у класса есть некое свойство? Эту функцию совершенно так же способен выполнить метод класса, возвращающий boolean.

спустя 27 минут [обр] 30-ый(59/584)[досье]

Ну надо заметить, что какой-то там Джошуа Блох это еще не показатель. Иногда подобные рационализаторы в процессе работы над докторской впадают в такой глубокий маразм, что в результате получается что-то типа JAI, где для универсальности и единообразности почти все методы имеют сингатуру вида:

public Object methodName(Object obj);

..или EJB 1.0-2.1, где если сингатуру каждого метода тебе удалось написать лишь в трех разных местах - считай повезло.

Про гениальные параграфы 55 и 63 от Лебедева я уж вообще не вспоминаю :-)

спустя 3 минуты [обр] Владимир Палант(253/4445)[досье]
Зачем использовать интерфейс без методов для отметки, что у класса есть некое свойство? Эту функцию совершенно так же способен выполнить метод класса, возвращающий boolean.
То есть у класса Object появились бы методы isSerializable, isCloneable итд? И каждый раз, когда кто-то бы придумывал новую фичу, туда бы добавлялись еще методы? Или для этих методов предполагается создавать собственные интерфейсы? Но тогда — что же мы выигрываем? :)
спустя 17 минут [обр] Данбала(2/63)[досье]

Даниэль Алиевский[досье]

Согласен. Я говорил: "К проверке класса допустимо прибегать, только если все прочие пути исчерпаны."

Я другое утверждаю. Не надо исчерпывать пути, проверку нужно делать, если она нужна, и не делать, если — нет. Извините за банальность. Просто нужно быть настолько грамотным, чтобы перед реализацией понимать эту нужность, а не оставлять на крайний случай.

И про это я говорил...

Я здесь не соглашаюсь с "исключительностью случая", "компромисностью" и "глубокой системностью". Задачи, где оправданно используется instanceof не исключение, они образуют отдельный класс, причем не настолько маленький, чтобы его не рассматривать.

Про маркер интерфейс. Я мог бы попытаться дать обобщенные характеристики тем системам, где я его использовал. Но это сложно и требует времени. Поэтому — простая задача: опишите метод с произвольным количесвом аргументов разных типов и без использования instanceof. Не важно, что будет реальным аргументом Object[], List или, например, Map. И, пожалуйста, давайте не будем вдаваться, зачем нужен такой метод, и имеет ли он право на существование. Просто опишите его. Только не словами, а кодом.

спустя 14 часов [обр] Даниэль Алиевский(35/125)[досье]

30-ый[досье]
Ну, Блох действительно кое-где утверждает спорные или устаревшие вещи - скажем, что следует избегать final при объявлении автоматических переменных, или что следует считать volatile "экспериментальным" решением. Но в целом, по-моему, это отличная книга, помогающая сильно улучшить качество кода. Пока мне не попадались авторы, которые бы резко критиковали Блоха. Я всего лишь переадресую упрек насчет "категоричного бреда" - не я его автор.

А что до JAI - это и правда какой-то перебор. Строчные имена операций, для которых даже нет констант - антипаттерн на антипаттерне.

Владимир Палант[досье]
Я полагаю, разработчики языка немало думали, прежде чем выбрать тот или иной вариант. Однако, результат все же получился очень неизящным - здесь я впервые услышал положительные отзывы о Cloneable. Почему, скажем, clone() не есть метод интерфейса Cloneable? Готов допустить, что как раз ради избавления от instanceof и приведения типа, когда нужно склонировать неизвестный объект (скажем, элемент List). Но тогда чем интерфейс лучше метода isCloneable? Почему в той же сериализации наличие методов (private!) readObject/writeObject само по себе влияет на поведение сериализации? Но при этом все же нужно реализовывать интерфейс-маркер Serializable! Не знаю, у меня все это вызывает ощущение крайней нелогичности. Возможно, это просто последствия экспериментальных поисков на очень ранней стадии зарождения Java, когда еще было совершенно неясно, что изящно, а что нет.

Интерфейс Comparable - вот это правильный и нормальный пример интерфейса. Да, новые фичи должны добавляться с помощью новых интерфейсов, с этим я вовсе не спорил. Но при грамотном использовании это вовсе не означает необходимости прибегать к instanceof.

Пример из моей практики. Однажды я реализовывал систему схем для цветового представления изображений. Схема - это свойство изображения, описывающее глубину цвета (1 бит, 8 бит на компоненту, 16 бит, float и т.п.) и набор компонент (монохромное, 3 компоненты RGB, 3 компоненты HSV, 1 компонента плюс палитра RGB, несколько компонент без четко заданной интерпретации и т.д.) Естественно, получилась достаточно сложная система классов и интерфейсов. При их использовании часто нужно было узнать, например, является ли схема картинки монохромной (многие алгоритмы изначально осмыслены только для однокомпонентных картинок, скажем, анализ кривизны поверхности z(x,y)), или это RGB (значит, можно быстро нарисовать), или это однобитовая монохромная картинка (значит, можно применять побитовые операции).

Теоретически, можно было бы применить instanceof - поддерживается ли такой-то интерфейс, например, float-точность. Но гораздо удобнее и изящнее оказалось использование информационных методов, вроде isRGB() или isMono(). Это позволило скрыть очень многие детали реализации (скажем, почти все мои классы доступны в пределах пакета, "снаружи" все сводится к нескольким простейшим классам). И оказалось гораздо логичнее. Скажем, с точки зрения наследования, 8-битовая картинка с RGB-палитрой является частным, специальным случаем однокомпонентной 8-битовой картинки: многие методы можно применять вообще без изменения. Но, разумеется, при этом она перестает быть монохромной! И метод isMono() это корректно отражает, чего не получилось бы с instanceof. При этом картинка остается однокомпонентной, что естественнее всего выражается методом componentCount() (а вовсе не реализацией интерфейса).

instanceof, в некотором роде, низкоуровневая проверка. Она проверяет именно внутреннее строение архитектуры пакетов - вещь, может быть, подлежащая в будущем изменениям. Развивая мой пример, интерфейс типа "ОднокомпонентнаяБайтоваяКартинка", реализуемый классом "Картинка", мог бы иметь метод getBytes(), возвращающий картинку в виде массива байтов. Допустим, мы вначале не предполагали работать с палитрами и активно использовали "instanceof ОднокомпонентнаяБайтоваяКартинка", чтобы убедиться, что картинка - монохромная (в противоположность RGB). Затем вызывали бы методы getBytes/setBytes, чтобы, например, выполнить сглаживание. Но в будущем у нас появились палитры. Картинка с палитрой по прежнему может обрабатываться методом getBytes() (эта функциональность у нее сохраняется!) Но вот сглаживание к ней применять бессмысленно. В итоге, мы попросту не смогли бы унаследовать картинки с палитрой от монохромных картинок без палитры, что было бы очень огорчительно.

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

Самый тривиальный пример разделения синтаксиса и семантики мы видим в стандартных коллекциях Java. Всякая коллекция, например, список обладает методами модификации вроде add или delete. Принадлежность класса к List - это синтаксический факт. А вот будут или не будут работать методы модификации - это уже факт семантический. Вполне могут и не заработать. Равно как и то, будут ли они синхронизированы относительно коллекции. Проверить через instanceof это невозможно.

Данбала[досье]

Я другое утверждаю. Не надо исчерпывать пути, проверку нужно делать, если она нужна, и не делать, если — нет. Извините за банальность. Просто нужно быть настолько грамотным, чтобы перед реализацией понимать эту нужность, а не оставлять на крайний случай.

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

Поэтому — простая задача: опишите метод с произвольным количесвом аргументов разных типов и без использования instanceof. Не важно, что будет реальным аргументом Object[], List или, например, Map. И, пожалуйста, давайте не будем вдаваться, зачем нужен такой метод, и имеет ли он право на существование. Просто опишите его. Только не словами, а кодом.

Не очень понял, что именно вы просите. Вот такой метод: PrintStream.printf из JDK 1.5. И зачем тут интерфейсы-маркеры или instanceof?

Кстати, вспомнил еще одну ситуацию, когда instanceof оправдан - проверки, является ли переменная массивом с элементами примитивного типа, вроде a instanceof byte[]. Тут просто нет альтернативы. А вот случай с массивом объектов уже обычно куда более спорный: как правило, намного изящнее использовать generics-коллекцию.

Powered by POEM™ Engine Copyright © 2002-2005