Главная » Java » Класс Class

0

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

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

Существует два способа получить объект Class: запросить его у имеющегося объекта методом getClass или искать его по уточненному (включающему все имена пакетов) имени статическим методом Class.forName.

Простейшие методы Class предназначены для перемещения по иерархии типов.

Приведенный ниже класс выводит такую иерархию для конкретного объекта Class:

public class TypeDesc {

public static void main(String[] args) { TypeDesc desc = new TypeDesc();

for (int i = 0; i << args.length; i++) {

try {

desc.printType(Class.forName(args[i]),  0);

} catch (ClassNotFoundException e) { System.err.print(e);  // сообщить об ошибке

}

}

// по умолчанию работать со стандартным выводом

public java.io.PrintStream out = System.out;

// используется в printType() для пометки имен типов

private static String[]

basic    = { "class",   "interface"   }, extended = { "extends", "implements"  };

public void printType(Class type, int depth) {

if (type == null) // супертип Object равен null return;

// вывести тип

for (int i = o; i << depth; i++)

out.print("  ");

String[] labels = (depth == 0 ? basic : extended); out.print(labels[type.isInterface()  ? 1 : 0] +  " "); out.println(type.getName());

// вывести интерфейсы, реализуемые классом Class[] interfaces = type.getInterfaces(); for (int i = o; i << interfaces.length; i++)

printType(interfaces[i], depth + 1);

// рекурсивный вызов для суперкласса

printType(type.getSuperclass(), depth + 1);

}

}

Данный пример просматривает имена, введенные в командной строке, и вызывает printType для каждого из них. Делать это необходимо в try-блоке на тот случай, если класс с заданным именем отсутствует. Ниже показан результат работы программы для класса java.util.Hashtable  (используется полное уточненное имя, поскольку этого требует метод forName):

class java.util.Hashtable

implements java.lang.Cloneable extends java.lang.Object

extends java.util.Dictionary

extends java.lang.Object

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

Метод printType выводит описание своего типа, а затем рекурсивно вызывает себя для распечатки свойств супертипов. Параметр depth показывает, на сколько уровней мы поднялись в иерархии типов; в зависимости от его значения каждая строка с описанием снабжается соответствующим  отступом. С каждым новым уровнем рекурсии это значение увеличивается.

При выводе типа метод isInterface определяет, является ли тип интерфейсом. Результат вызова используется для выбора префикса — пояснительной надписи. В самом низу иерархии типов, где значение depth равно 0, выводятся надписи “class” и “interface”; типы, находящиеся выше в иерархии, расширяют или реализуют свои исходные типы, поэтому используются термины “extends” и “implements”. Именно для этого и создаются массивы Basic и Extended. После выбора нужного префикса имя типа выводится методом getName. Конечно, класс Class содержит метод toString, однако в этом методе уже использован префикс “class” или “interface”. Мы хотим сами контролировать префикс, и потому пришлось создать собственную реализацию метода.

После вывода описания типа метод printType осуществляет рекурсивный вызов себя самого. Сначала определяются все интерфейсы, реализуемые исходным типом. /Если тип, для которого выводится информация, представляет собой интерфейс, то он расширяет, а не реализует свои интерфейсы-супертипы. Тем не менее, учет таких подробностей привел бы к неоправданному усложнению кода./ Затем выводится расширяемый им суперкласс (если он существует). Постепенно метод доходит до объекта Class класса Object, который не реализует никаких интерфейсов и для которого метод getSuperClass возвращает null;

на этом рекурсия завершается.

Упражнение 13.1

Модифицируйте TypeDesc, чтобы избежать вывода информации о классе Object. Эти сведения избыточны, поскольку каждый объект в конечном счете расширяет класс Object. Используйте ссылку на объект Class для типа Object.

Объект Class может воспользоваться методом newInstance для создания нового экземпляра (объекта) представляемого  им типа. При этом вызывается безаргументный конструктор класса или возбуждается исключение NoSuch MethodError, если класс не имеет безаргументного  конструктора. Если класс или безаргументный конструктор недоступны (не являются открытыми или находятся в другом пакете), возбуждается исключение IllegalAccessException. Если класс является абстрактным, или представляет собой интерфейс, или создание завершилось неудачно по какой-то другой причине, возбуждается исключение InstantiationException. Создавать новые объекты подобным образом оказывается удобно, когда вы хотите написать универсальный код и позволить

пользователю задать нужный класс. Например, в программе тестирования алгоритмов сортировки, приведенной в разделе “Проектирование  расширяемого класса”, пользователь мог ввести имя тестируемого класса и использовать его в качестве параметра для вызова forName. Если введенное имя класса окажется допустимым, можно вызвать метод newInstance для создания объекта этого типа. Метод main для универсального класса SortDouble выглядит следующим образом:

static double[] testData = { 0.3, 1.3e-2. 7.9. 3.17, );

public static void main(String[] args) {

try {

for (int arg = 0; arg << args.length; arg++) { String name = args[arg];

Class classFor = Class.forName(name); SortDouble sorter

= (SortDouble)classFor.newInstance();

SortMetrics metrics

= sorter.sort(testData); System.out.println(name + ": " + metrics); for (int i = 0; i << testData.length; i++)

System.out.println("\t" + testData[i]);

}

} catch (Exception e) {

System.err.print(e);     // сообщить об ошибке

}

}

Этот метод почти совпадает с BubbleSortDouble.main, однако из него исключены все имена типов. Он применим к любому типу, объекты которого могут использоваться в качестве объектов SortDouble и который содержит безаргументный  конструктор. Теперь нам не придется переписывать метод main для каждого алгоритма сортировки — универсальный main годится для всех случаев. Все, что нужно сделать, — выполнить команду

java SortDouble TestClass …

для любого класса-сортировщика (наподобие BubbleSortDouble);  класс будет загружен и выполнен.

Источник: Арнольд К., Гослинг Д. – Язык программирования Java (1997)

По теме:

  • Комментарии