Студопедия
Главная страница | Контакты | Случайная страница

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Распаковщик методов класса

Читайте также:
  1. X. РЕЗУЛЬТАТЫ ЛАБОРАТОРНЫХ И СПЕЦИАЛЬНЫХ МЕТОДОВ ИССЛЕДОВАНИЯ
  2. Анализ методов перевода профессиональными переводчиками
  3. В 11 классах
  4. в 11 классах
  5. В наших примерах мы ни разу не рассматривали конструкторы классов, поэтому при создании экземпляров наших классов вызывался конструктор класса object.
  6. Вес функции; вес класса эквивалентности
  7. Влияние методов тренировки на динамику скоростно-силовых показателей
  8. Влияние физических методов лечения на резистентность организма
  9. Вторая группа методов установления личности включает в себя методы отождествления личности по внешности (габитология).
  10. Выбор конкретных методов опроса

Вам редко будет нужно использовать инструменты рефлексии напрямую; они находятся в языке, для поддержки других расширений Java, таких как сериализация объектов (Глава 11), JavaBeans (Глава 13) и RMI (Глава 15). Однако, существуют случаи, когда абсолютно необходима возможность динамической распаковки информации о классе. Очень полезный инструмент для этого - распаковщик методов класса. Как было упомянуто выше, просмотр исходных кодов описания класса или онлайн - документация показывает только те методы, которые определены либо перекрыты внутри этого класса. Но Вам может быть доступно гораздо больше информации из базовых классов. Определение их является занятием скучным и расточительным по времени[60]. К счастью, рефлексия предоставляет способ написать простой инструмент, который автоматически покажет Вам весь интерфейс. Вот как он работает:

//: c12:ShowMethods.java// Использование рефлексии для отображения все методов // класса, включая определенные // базовом классе.import java.lang.reflect.*; public class ShowMethods { static final String usage = "usage: \n" + "ShowMethods qualified.class.name\n" + "To show all methods in class or: \n" + "ShowMethods qualified.class.name word\n" + "To search for methods involving 'word'"; public static void main(String[] args) { if(args.length < 1) { System.out.println(usage); System.exit(0); } try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); if(args.length == 1) { for (int i = 0; i < m.length; i++) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) System.out.println(ctor[i]); } else { for (int i = 0; i < m.length; i++) if(m[i].toString().indexOf(args[1])!= -1) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) if(ctor[i].toString().indexOf(args[1])!= -1) System.out.println(ctor[i]); } } catch(ClassNotFoundException e) { System.err.println("No such class: " + e); } }} ///:~

Методы объекта Class getMethods() и getConstructors() возвращают массивы методов - Method и конструкторов - Constructor, соответственно. Каждый из этих классов имеет методы для разделения имен, аргументов и возвращаемых значений методов, которые они представляют. Но Вы можете также использовать метод toString(), как это сделано в примере, для получения строки String с полной сигнатурой метода. Остаток кода - просто раскрытие информации из командной строки, определяющая совпадает ли соответствующая сигнатура с результирующей строкой (используя indexOf()), и печатает результаты.

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

Чтобы проверить, как это работает, запустите:

java ShowMethods ShowMethods

В результате создается список, который содержит публичный конструктор по умолчанию, хотя Вы видите из кода, что там конструктор не определен. Тот конструктор, который Вы видите, является элементом, который автоматически генерируется компилятором. Если Вы сделаете ShowMethods не- public классом, то генерируемый по умолчанию конструктор больше не будет отображаться в списке результатов. Этому конструктору автоматически устанавливается такой же доступ, какой определен для класса.

Результаты работы ShowMethods немного скучные. Например, вот - часть результатов полученных с вызова java ShowMethods java.lang.String:

public boolean java.lang.String.startsWith(java.lang.String,int)public boolean java.lang.String.startsWith(java.lang.String)public boolean java.lang.String.endsWith(java.lang.String)

Будет гораздо лучше, если префиксы типа java.lang будут отброшены. Класс StreamTokenizer описанный в предыдущей главе поможет создать инструмент для решения этой проблемы:

//: com:bruceeckel:util:StripQualifiers.javapackage com.bruceeckel.util;import java.io.*; public class StripQualifiers { private StreamTokenizer st; public StripQualifiers(String qualified) { st = new StreamTokenizer(new StringReader(qualified)); st.ordinaryChar(' '); // Хранит пробелы } public String getNext() { String s = null; try { int token = st.nextToken(); if(token!= StreamTokenizer.TT_EOF) { switch(st.ttype) { case StreamTokenizer.TT_EOL: s = null; break; case StreamTokenizer.TT_NUMBER: s = Double.toString(st.nval); break; case StreamTokenizer.TT_WORD: s = new String(st.sval); break; default: // единичный символ в ttype s = String.valueOf((char)st.ttype); } } } catch(IOException e) { System.err.println("Error fetching token"); } return s; } public static String strip(String qualified) { StripQualifiers sq = new StripQualifiers(qualified); String s = "", si; while((si = sq.getNext())!= null) { int lastDot = si.lastIndexOf('.'); if(lastDot!= -1) si = si.substring(lastDot + 1); s += si; } return s; }} ///:~

Для облегчения повторного использования, этот класс расположен в com.bruceeckel.util. Как Вы видите, он использует манипуляции с StreamTokenizer и String для решения проблемы.

Новая версия этой программы использует приведенные выше классы и дает чистые результаты:

//: c12:ShowMethodsClean.java// ShowMethods с отброшенными префиксамиimport java.lang.reflect.*;import com.bruceeckel.util.*; public class ShowMethodsClean { static final String usage = "usage: \n" + "ShowMethodsClean qualified.class.name\n" + "To show all methods in class or: \n" + "ShowMethodsClean qualif.class.name word\n" + "To search for methods involving 'word'"; public static void main(String[] args) { if(args.length < 1) { System.out.println(usage); System.exit(0); } try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); // Конвертирует в массив "очищенных" строк: String[] n = new String[m.length + ctor.length]; for(int i = 0; i < m.length; i++) { String s = m[i].toString(); n[i] = StripQualifiers.strip(s); } for(int i = 0; i < ctor.length; i++) { String s = ctor[i].toString(); n[i + m.length] = StripQualifiers.strip(s); } if(args.length == 1) for (int i = 0; i < n.length; i++) System.out.println(n[i]); else for (int i = 0; i < n.length; i++) if(n[i].indexOf(args[1])!= -1) System.out.println(n[i]); } catch(ClassNotFoundException e) { System.err.println("No such class: " + e); } }} ///:~

Класс ShowMethodsClean очень похож на предыдущий ShowMethods, за исключением того, что он берет массивы Method и Constructor и конвертирует их в единичный массив строк String. Каждый из этих объектов String пропускается через StripQualifiers.Strip() для удаления всех префиксов метода.

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

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

Резюме

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

Однако, использование вызовов полиморфных методов как они понимаются, требует чтобы у Вас было определение базового класса, т.к. по некоторым причинам при расширении Вашей программы Вы можете выяснить, что базовый класс не включает в себя метода, который Вам нужен. Если базовый класс приходит из библиотеки, либо просто разрабатывается кем-то другим, решением проблемы является RTTI: Вы можете наследовать новый тип и добавить дополнительный метод. В другом месте кода Вы сможете определить этот тип и вызвать соответствующий метод. Это не уничтожает полиморфизм или возможность расширения Вашей программы, т.к. добавление нового типа не требует от Вас охотиться за выражениями switch в Вашей программе. Однако, когда Вы добавляете новый код в основное тело, для расширения возможностей, Вам нужно использовать RTTI для определения соответствующего типа.

Расширение возможностей базового класса означает, что для пользы одного конкретного класса все остальные классы, наследуемые от этого базового класса должны реализовывать бесполезную заглушку метода. Это делает интерфейс менее ясным и досаждает тем, кто должен перекрывать абстрактные методы, когда они наследуются от базового класса. Например, есть иерархия классов представляющих музыкальные инструменты. Предположим, что Вы хотите очистить клапаны соответствующих музыкальных инструментов в Вашем оркестре. Один из вариантов - реализовать метод clearSpitValve() в базовом классе Instrument, но это не верно, т.к. это предполагает что классы инструментов Percussion (ударные) и Electronic (электронные) также имеют клапаны. RTTI предоставляет более подходящее решение в этом случае т.к. Вы можете расположить этот метод в специальном классе (Wind в нашем случае). Однако, более подходящее решение - это создание метода prepareInstrument() в базовом классе, но Вы можете не понять этого, когда в первый раз решаете эту проблему, и ошибочно предположить, что Вам необходимо использовать RTTI.

Наконец, RTTI иногда решает проблемы эффективности. Если Ваш код красиво использует полиморфизм, но оказывается, что один из Ваших объектов выполняет основные цели совершенно неэффективно, Вы можете определять этот тип используя RTTI и написать основанный на вариантах код для увеличения производительности. Будьте, однако, осторожны, и не гонитеть сразу за эффективностью. Это соблазнительная ловушка. Лучше всего - сначала заставить программу работать, затем определить достаточно ли быстро она работает, и только затем пытаться определить неэффективные блоки программы с помощью профилера.




Дата добавления: 2015-09-11; просмотров: 84 | Поможем написать вашу работу | Нарушение авторских прав

Альтернатива Externalizable | Использование устойчивости | StreamTokenizer | StringTokenizer | Проверка стиля капитализации | Упражнения | Необходимость RTTI | Объект Class | Проверка перед приведением типа | Синтаксис RTTI |


lektsii.net - Лекции.Нет - 2014-2025 год. (0.009 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав