Слайд 1Рефлексия
© Составление, Гаврилов А.В., 2012
Слайд 2План лекции
Понятие рефлексии
Участники механизма рефлексии
Получение информации о классе
Слайд 3Понятие рефлексии
Рефлексия (от лат. reflexio – обращение назад) – обращение
субъекта на себя самого, на свое знание или на свое
собственное состояние
Рефлексия в Java – возможность программы анализировать саму себя, взаимодействуя с виртуальной машиной Java (JVM)
Слайд 4Возможности механизма рефлексии
Загрузка типов во время исполнения программы
Исследование структуры типов
и их элементов
Создание экземпляров классов
Вызов методов
Загрузка классов из набора байтов
Слайд 5Участники механизма рефлексии
Класс java.lang.Class
Класс является метаклассом по отношению к другим
типам
Экземпляры класса Class описывают классы и интерфейсы выполняемого приложения
Методы класса
Class позволяют исследовать содержимое описываемого класса и его свойства
Класс java.lang.ClassLoader
Реализует механизмы загрузки классов
Слайд 6Участники механизма рефлексии
Пакет java.lang.reflect
Содержит ряд дополнительных и вспомогательных классов
Field
Описывает поле
объекта
Method
Описывает метод объекта
Constructor
Описывает конструктор объекта
Modifier
Инкапсулирует работу с модификаторами
Array
Инкапсулирует работу с
массивами
Слайд 7Получение представления класса
Метод Class Object.getClass()
Возвращает ссылку на представление класса, экземпляром
которого является объект
Псевдополе Object.class
Ссылка на представление указанного класса
Метод static Class
Class.forName(...)
Возвращает ссылку на представление класса, полное имя которого указывается параметром типа String
Слайд 8Получение представления класса
Метод Class[] Class.getClasses()
Возвращает ссылку на массив ссылок на
объекты Class вложенных типов
Метод Class Class.getDeclaringClass()
Для вложенных типов возвращает ссылку
на объект Class внешнего типа
Метод Class[] Class.getInterfaces()
Возвращает ссылки на описания интерфейсов, от которых наследует тип
Метод Class Class.getSuperclass()
Возвращает ссылку на описание родительского класса
Слайд 9Получение информации о классе
import java.lang.reflect.*;
class ListMethods {
public static void
main(String[] argv)
throws
ClassNotFoundException {
Class c = Class.forName(argv[0]);
Constructor[] cons = c.getConstructors( );
printList("Constructors", cons);
Method[] meths = c.getMethods( );
printList("Methods", meths);
Field[] fields = c.getFields();
printList("Fields", fields);
}
static void printList(String s, Object[] o) {
System.out.println("*** " + s + " ***");
for (int i = 0; i < o.length; i++)
System.out.println(o[i].toString( ));
}
}
Слайд 10Возможности класса Class
Загрузка класса в JVM по его имени
static Class
forName(String name)
Определение вида типа
boolean isInterface()
boolean isLocalClass()
Получение родительских типов
Class getSuperclass()
Class[] getInterfaces()
Получение вложенных типов
Class[] getClasses()
Создание объекта
Object newInstance()
Слайд 11Возможности класса Class
Получение списка всех полей и конкретного поля по
имени
Field[] getFields()
Field getField(...)
Получение списка всех методов и конкретного метода по
имени и списку типов параметров
Method[] getMethods()
Method[] getMethod(...)
Получение списка всех конструкторов и конкретного конструктора по списку типов параметров
Constructor[] getConstructors()
Constructor getConstructor(...)
Слайд 12Передача параметров в методы
Поскольку на момент написания программы типы и
даже количество параметров неизвестно, используется другой подход:
Ссылки на все параметры
в порядке их следования помещаются в массив типа Object
Если параметр имеет примитивный тип, то в массив помещается ссылка на экземпляр класса-оболочки соответствующего типа, содержащий необходимое значение
Возвращается всегда тип Object
Для ссылочного типа используется приведение типа или рефлексивное исследование
Для примитивных типов возвращается ссылка на экземпляр класса-оболочки, содержащий возвращенное значение
Слайд 13Создание экземпляров классов
Метод Object Class.newInstance()
Возвращает ссылку на новый экземпляр класса,
используется конструктор по умолчанию
Метод
Object Constructor.newInstance(
Object[] initArgs)
Возвращает ссылку на
новый экземпляр класса, с использованием конструктора и указанными параметрами конструктора
Слайд 14Вызов методов
Прямой вызов
Если на момент написания кода известен класс-предок загружаемого
класса
Вызов через экземпляр класса Method
Object Method.invoke(Object obj, Object[] args)
obj –
ссылка объект, у которого должен быть вызван метод
принято передавать null, если метод статический
args – список параметров для вызова методов
Слайд 15Вызов статического метода
import java.lang.reflect.*;
public class Main {
public static void
main(String[] args) {
if (args.length == 3) {
try {
Class c = Class.forName(args[0]);
Method m = c.getMethod(args[1], new Class [] {Double.TYPE});
Double val = Double.valueOf(args[2]);
Object res = m.invoke(null, new Object [] {val});
System.out.println(res.toString());
} catch (ClassNotFoundException e) {
System.out.println("Класс не найден");
} catch (NoSuchMethodException e) {
System.out.println("Метод не найден");
} catch (IllegalAccessException e) {
System.out.println("Метод недоступен");
} catch (InvocationTargetException e) {
System.out.println("При вызове возникло исключение");
}
}
}}
Слайд 16Нововведения Java5
© Составление, Гаврилов А.В., 2012
Слайд 17План лекции
Обзор нововведений
Статический импорт
Автобоксинг
Переменное количество аргументов
Параметризованные типы
Слайд 18Что же произошло?
Автоупаковка и автораспаковка (автобоксинг)
Настраиваемые типы
Улучшенный цикл for
Аргументы переменной
длины
Перечислимые типы
Метаданные
Статический импорт
Форматированный ввод/вывод
Изменения пакетов
Слайд 19Проблема
Имеется:
Хотелось бы:
hypot = Math.sqrt(Math.pow(side1, 2)
+ Math.pow(side2, 2));
hypot = sqrt(pow(side1, 2)
+ pow(side2, 2));
Слайд 20Статический импорт
Импорт элемента типа
import static pkg.TypeName.staticMemberName;
Импорт всех элементов типа
import static
pkg.TypeName.*;
import static java.lang.Math.sqrt;
import static java.lang.Math.pow;
import static java.lang.Math.*;
Слайд 21Особенности статического импорта
Повышает удобство написания программ и уменьшает объем кода
Уменьшает
удобство чтения программ
Приводит к конфликтам имен
Мораль: рекомендуется к использованию только
когда действительно необходим
Слайд 22Проблема
Имеется:
Хотелось бы:
List list = new LinkedList();
list.add(new Integer(1));
list.add(new Integer(10));
List list =
new LinkedList();
list.add(1);
list.add(10);
Слайд 23Автоупаковка / автораспаковка
Автоупаковка – процесс автоматической инкапсуляции данных простого типа
в экземпляр соответствующего ему класса-обертки в случаях, когда требуется значение
ссылочного типа
Автораспаковка – процесс автоматического извлечения примитивного значения из объекта-упаковки в случаях, когда требуется значение примитивного типа
List list = new LinkedList();
list.add(1);
int b = (Integer)list.get(0) + 10;
Слайд 24Особенности автоупаковки
Происходит при присваивании, вычислении выражений и при передаче параметров
Объекты
создаются без использования ключевого слова new
Объекты создаются!
Вотще полагать, что примитивные
типы не нужны
Автоупаковка требует существенных ресурсов
Злоупотреблять автоупаковкой вообще не стоит!
Integer i = 15;
Слайд 25Проблема
Имеется:
Хотелось бы:
int s1 = sum(new int[] {1, 2});
int s2 =
sum(new int[] {1, 2, 3});
int s1 = sum(1, 2);
int s2
= sum(1, 2, 3, 4);
Слайд 26Переменное количество аргументов
Пример метода
Пример вызова
int sum(int ... a) {
int
s = 0;
for (int i = 0; i
a.length; i++)
s += a[i];
}
int s2 = sum(1, 2, 3);
int s1 = sum(new int[] {1, 2});
Слайд 27Особенности переменного количества аргументов
Внутри там все равно живет массив…
Аргумент переменной
длинны в методе может быть только один
Аргумент переменной длинны должен
быть последним в списке аргументов метода
В сочетании с перегрузкой методов способен приводить к изумительным ошибкам компиляции в виду неоднозначности кода
Слайд 28Проблема
Имеется:
Хотелось бы:
List list= new LinkedList ();
list.add(10);
list.add(5);
list.add((Integer)list.get(0) + (Integer)list.get(1));
List list= new
LinkedList();
list.add(10);
list.add(5);
list.add(list.get(0) + list.get(1));
Слайд 29Параметризованные типы
Параметризованные типы (настраиваемые типы, generic types)
Позволяют создавать классы, интерфейсы
и методы, в которых тип обрабатываемых данных задается как параметр
Позволяют
создавать более компактный код, чем универсальные (обобщенные) типы, использующие ссылки типа Object
Обеспечивают автоматическую проверку и приведение типов
Позволяют создавать хороший повторно используемый код
Слайд 30Скромный пример
Пример класса
Пример использования
class Generic {
T obj;
Generic(T o)
{obj = o;}
T getObj() {return obj;};
}
Generic iObj;
iObj = new
Generic(33);
int i = iObj.getObj() + 10;
Слайд 31Особенности параметризованных типов
Использовать примитивные типы в качестве параметров-типов нельзя
Если одинаковые
настраиваемые типы имеют различные аргументы, то это различные типы
Обеспечивается более
жесткий контроль типов на стадии компиляции
Слайд 32Общий синтаксис
Объявление настраиваемого типа
class имяКласса {...}
Создание ссылки и объекта настраиваемого
типа
имяКласса имяПеременной = new имяКласса(список-аргументов);
class Generic2 {...}
Generic2 gObj
= new
Generic2(10, "ok");
Слайд 33Ограниченные типы
Ограничение типа позволяет использовать у ссылок методы и поля,
доступные в типе-ограничителе
Типы, не наследующие от указанного, не могут быть
использованы при создании объектов
Как имя типа может быть указан интерфейс!!!
Как имя типа может быть указан ранее введенный параметр!!!
class Stats {
T[] nums;
Stats(T[] o) {nums = o;}
double average() {
double sum = 0.0;
for(int i = 0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
}
class Generic3> {...}
Слайд 34Метасимвольный аргумент
Что делать при передаче экземпляров параметризованных типов в методы,
т.е. как писать сигнатуру?
Для этого используется метасимвол, обозначающий произвольный тип-параметр
class
Generic {
...
boolean compare(Generic> o) {
return o.getObj() == obj;
}
Слайд 35Метасимвол с ограничениями
Ограничение сверху
sub>
Тип sub не допускается
Слайд 36Параметризованные методы
Методы могут иметь собственные типы-параметры
Фактические аргументы, передаваемые в формальные
аргументы, имеющие тип-параметр, будут проверяться на соответствие типу, причем на
этапе компиляции
Пример метода
Пример использования
public static > T min(T[] v) {
T min = v[0];
for (int i = 1; i < v.length; i++)
if (min.compareTo(v[i]) > 0)
min = v[i];
return min;
}
System.out.println(min(new Integer[] {10, 15, 5}));
Слайд 37Ряд особенностей
Конструкторы могут быть параметризованными (даже если сам класс таковым
не является)
Интерфейсы могут быть параметризованными
Нельзя создавать объекты, используя типы-параметры
Статические члены
класса не могут использовать его типы-параметры
Настраиваемый класс не может расширять класс Throwable
От настраиваемых типов можно наследовать, есть ряд особенностей
Слайд 38Ряд особенностей
Нельзя создать массив типа-параметра
Массивов элементов конкретной версии параметризованного типа
не бывает
class Generic {
T[] vals; //OK
Generic(T[] nums) {
//vals = new T[10]; //Не есть правильно!
vals = nums; //OK
}
}
//Generic[] gens = ew Generic[10];//Nicht OK
Generic>[] gens = new Generic>[10];
Слайд 39И как же это работает?
Механизм стирания
В реальном байт-коде никаких настраиваемых
типов в целом-то и нет…
Информация о настраиваемых типах удаляется на
стадии компиляции
Именно компилятор осуществляет контроль безопасности приведения типов
А внутри после компиляции все те же «обобщенные» классы, явные приведения типов и прочее, и прочее…
Слайд 40Ошибки неоднозначности
«Логически правильный» код
Оказывается неверным с точки зрения компилятора
И это
– самый простой пример…
public class Test {
T first(T[]
arr) {
return arr[0];
}
Object first(Object[] arr) {
return arr[0];
}
}
Test.java:6: first(T[]) is already defined in Test
Object first(Object[] arr) {
^