Информация о типе

  • Класс Class<T> ─ информация о типе
    • Type token
  • Предоставляемая информация
    • Структура класса
    • Структура наследования
    • Проверки времени выполнения
    • ...

Получение информации о типе

  • Во время исполнения
  • Во время компиляции
    • Type.class
    • Примеры
      • ArrayList.class
      • int.class
      • int[].class
  • Предопределенные

Типы типов

  • Для определения типов служат методы вида

Общая информация о классе

Место определения класса

Приведение типов

Информация о члене класса

Модификаторы

Модификаторы (Java 20+)

Поля

Поля и модификаторы доступа

Свойства полей

Методы

Свойства методов

Конструкторы

Свойства конструкторов

Классы и интерфейсы

Доступ к закрытым членам

Пример: листинг класса

Class c = …;
for (Field m : c.getDeclaredFields()) {
    System.out.println(m);
}
for (Constructor m : c.getDeclaredConstructors()) {
    System.out.println(m);
}
for (Method m : c.getDeclaredMethods()) {
    System.out.println(m);
}

Пример: поля класса String

  • private final byte[] value
    private final byte   coder
    static final boolean COMPACT_STRINGS
    static final byte LATIN1
    static final byte UTF16
    private int hash
    private boolean hashIsZero
    private static final char REPL
    private static final long serialVersionUID
    private static final ObjectStreamField[] 
            serialPersistentFields
    public static final Comparator 
            CASE_INSENSITIVE_ORDER
    

Пример: создание экземпляра

// Получение класса
Class<Random> clazz = Random.class;
// Получение конструктора
Constructor<Random> c =
    clazz.getConstructor(long.class);
// Создание экземпляра
Random r = c.newInstance(100L);
// Проверка
System.out.println(r.nextInt());

Массивы

Массивы как типы

  • Имя типа массива
    • [имя_типа_элемента
  • Методы

Двоичные имена для типов

  • Специальное кодирование
    class Lclass;
    boolean Z
    byte B
    char C
    double D
    float F
    int I
    long J
    short S

Перечисления

Записи

Sealed Classes

Загрузчики классов

Содержание

Загрузчики классов

Дерево загрузчиков

  • Загрузчики образуют дерево
  • Загрузчики в разных ветвях могут загрузить разные классы с одним полным именем

Дополнительные возможности

  • Получение родителя
  • Загрузка ресурсов

Загрузчики и классы

Реализации загрузчиков

  • Класс URLClassLoader
    • Загружает классы из нескольких мест, заданных URL

Пример

  • Загрузка класса
    • URL url = Path.of(".").toUri().toURL();
      ClassLoader cl = new URLClassLoader(new URL[]{url});
      Class<?> c = cl.loadClass("Test");
      
  • Вызов main
    • Method m = c.getMethod("main", String[].class);
      m.invoke(null, (Object) new String[]{"hello"});
      
  • Запуск как Runnable
    • Constructor<?> constructor = c.getConstructor();
      Runnable instance = 
              (Runnable) constructor.newInstance();
      instance.run();
      

Метаданные (аннотации)

Содержание

Метаданные и аннотации

  • Метаданные
    • Информация, не влияющая на выполнение программы
  • Аннотации
    • Представление метаданных в Java
  • Пакет java.lang.annotation

Пример определения аннотации

public @interface TODO {
    int id();
    String synopsis();
    String assignee() default "[unassigned]";
    String date() default "[undefined]";
}

Пример использования аннотации

@TODO(
    id = 123,
    synopsis = "Implement",
    assignee = "Georgiy Korneev",
    date = "15.03.2024"
)
public static void giveLecture(String id, String title)

Типы аннотаций

  • Обыкновенная
  • Маркерная – без параметров
    • Может использоваться без скобок
    • @MarkerAnnotation
    • @MarkerAnnotation()
  • Одиночная – с одним параметром
    • Параметр должен называться value()
    • Без указания имени параметра
    • @SingletonAnnotation("Hello")
    • @SingletonAnnotation(value="Hello")

Типы параметров аннотаций

  • Основные типы
    • Примитивные типы
    • String
    • Class
    • Перечислимый тип
    • Аннотация
  • Массив вышеперечисленного

Время жизни аннотации

Область применения

Стандартные аннотации

  • @Inherited
    • Наследование при переопределении метода / класса
  • @Documented
    • Попадает в JavaDoc аннотированного документа
    • Применяется для аннотаций, изменяющих поведение
  • @SuppressWarnings({types})
    • Подавляет предупреждения указанного типа
  • @Deprecated
    • Помечает метод/класс как устаревший

Аннотации во время выполнения

Пример: Тестирование (1)

  • Аннотация
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Test { }
    
  • Пример использования
    public class SimpleTest {
        @Test public static void testXXX();
        @Test public static void testYYY();
    }
    

Пример: Тестирование (2)

  • Запуск тестов
    for (Method m : clazz.getMethods()) {
      if (m.isAnnotationPresent(Test.class)) {
         try {
            m.invoke(null);
         } catch (Throwable e) {
            System.out.printf("Test %s failed: %s %n",
                m, e.getCause());
         }
      }
    }
    

Пример: Конфигурация

  • Аннотация
    • @Target(ElementType.TYPE_USE)
      public @interface Attr { String value; }
      public @interface Array { String value default "NONE"; }
      
  • Описание
    • @Array Map<
          @Attr("id") Integer,
          @Array("items") List<@Attr("value") String>
      > values;
      
  • Значение
    • [
          {id: 123, {items: [{value: "v1"}, {value: "v2"}]},
          {id: 456, {items: []}
      ]
      

Информация о параметрах типов

  • Информация о конкретных параметрах типов стирается
  • Информация о зависимостях типов сохраняется

Получение информация о ПТ

Представление информации о ПТ

Параметризованные классы

Переменные типа

Wildcards

Массивы

  • Тип элемента – переменная типа
    • Пример: T[]
  • Тип элемента – параметризованный тип
    • Пример: Set<T>[]
  • Интерфейс GenericArrayType

Proxy

  • Генерация классов во время исполнения
    • Фиктивные классы
    • Реализующие требуемые интерфейсы
  • Класс Proxy

Делегация вызовов

Методы Proxy

Пример: профайлер (1)

  • Класс
    public class Profiler implements
            InvocationHandler {
        // Экземпляр Proxy
        private final Object instance;
        // Реальная реализация
        private final Object impl;
        ...
    }
    

Пример: профайлер (2)

  • Конструктор
    public Profiler(Class[] i8s, Object impl) {
        this.impl = impl;
        instance = Proxy.newProxyInstance(
            null, i8s, this);
    }
    
  • Создание экземпляра
    public Object getInstance() {
        return instance;
    }
    

Пример: профайлер (3)

  • Основной метод
    public Object invoke(Object proxy,
            Method method, Object[] args
    ) throws IllegalAccessException,
            InvocationTargetException {
        System.out.println(
            "Calling " + method + " on " + impl);
        return method.invoke(impl, args);
    }
    

Пример: профайлер (4)

  • Применение
    public static void main(String[] args) {
        Integer i1 = new Integer(3);
        Profiler profiler = new Profiler(
            new Class[]{Comparable.class}, i1);
        Comparable i2 =
            (Comparable) profiler.getInstance();
        System.out.println(i2.compareTo(i1));
    }
    

Выводы

  • Reflection позволяет
    • Анализировать классы по время исполнения
    • Загружать классы по имени
    • Создавать экземпляры классов
    • Вызывать методы классов по имени
    • Оперировать значениями полей по имени
    • Создавать и оперировать с массивами по типу элемента
    • Создавать proxy для интерфейсов
    • Работать с аннотациями

Осталось за кадром

  • Обработка аннотаций во время компиляции
    • Пакет javax.annotation.processing
    • Проверка и генерация кода
  • Модель языка
    • Пакет javax.lang.model
    • Информация о типах
  • Поддержка лямбд и динамических языков
  • Генерация байткода

Ссылки

Вопросы

???