Страницы

Поиск по вопросам

Показаны сообщения с ярлыком rtti. Показать все сообщения
Показаны сообщения с ярлыком rtti. Показать все сообщения

четверг, 9 января 2020 г.

RTTI. Как обратить к статическому полю класса, имея объект Class для него?

#java #рефлексия #rtti


Имеем класс Allocator, который имеет метод allocate(Class). Как без знания того,
объект Class какого класса пришёл, вытащить из него поле с возможностью его изменить?

Вариант с использованием objOFclassClass.newInstance() не подойдёт. Охота без лишних
затрат памяти на создание объекта обойтись.  

class X {
    public static boolean f = false;
}

class Y {
    public static boolean f = false;
}

class Z {
    public static boolean f = false;
}

class Allocator {
    public static void allocate (Class c) throws Exception{
        // Предполагая, что (c.name == "X" или "Y" или "Z"), как сделать что то в духе:
        // c.setStaticField("f",true);  ???
    }
}

    


Ответы

Ответ 1



С помощью метода getDeclaredField получаем объект класса Field Изменяем поле с помощью вызова метода set у этого объекта Field field = c.getDeclaredField("имя_поля"); field.set(null, true /* новое значение */); Код на Ideone

вторник, 24 декабря 2019 г.

Почему обращение к SomeClass.class не вызывает инициализацию класса SomeClass?

#java #rtti


Вопрос такой: Почему использование A.class не вызывает инициализацию класса А?  

Хотя, учитывая, что подготовка класса к использованию состоит из трёх этапов, на
первом из которых создаётся объект Class, и лишь третий из которых -- непосредственная
инициализация, вопрос логичнее поставить так:
Почему вызов Class.forName("test.A"); провоцирует инициализацию класса А?
 Может сам метод Class.forName(String) создан так, чтобы целиком насильно загружать
классы? 

package test;

class A {
    static {
        System.out.println("A is initialized");
    }
}

public class Test{
    public static void main(String[] args) throws Exception {
        Class c = A.class;
        System.out.println("whoa...");
        c = Class.forName("test.A");
    }
}




Expected output:
    A is initialized
    whoa...

Real output:
    whoa...
    A is initialized

    


Ответы

Ответ 1



В JLS 12.4.1 написано, что статическая инициализация происходит перед: Созданием экземпляра класса; Вызовом статического метода класса; Присвоением статического поля класса; Использованием неконстантного статического поля класса; Выполнением оператора assert в классах верхнего уровня. А при вызове метода forName() ClassLoader осуществляет принудительную инициализацию.

Ответ 2



В JLS 12.4.1 написано: Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. Что в переводе: Вызов некоторых отражающих методов в классе Class и в пакете java.lang.reflect также вызывает инициализацию класса или интерфейса. А в этой документации для метода forName() класса Class чётко написано: A call to forName("X") causes the class named X to be initialized. Что в переводе: Вызов forName («X») вызывает инициализацию класса с именем X. Соответственно, всё оказалось верно! Метод Class.forName(String) действительно насильно загрузит класс.

среда, 27 ноября 2019 г.

Зачем нужен dynamic_cast?

#c++ #классы #указатели #rtti


class A;
class B : public A;

B* b;
A* a = dynamic_cast(b);  // 1
A* a = (A*)b;  // 2


Для чего нужен dynamic_cast? Какие преимущества имеет запись 1 по сравнению с записью
2 и какие недостатки? В каких случаях какой вариант следует использовать? 
    


Ответы

Ответ 1



Вообще-то, в том варианте, как вы записали, не нужно приводить ничего, потому что объект производного класса уже ЯВЛЯЕТСЯ объектом базового. Так что можно просто писать A * a = b; А вот если наоборот - B * b = a; начинаются проблемы. Потому что обычно это говорит о плохом проектировании. Вы хотите использовать именно потомка там, где используется только предок. Т.е. по сути дописать какое-то хитрое поведение там, где о нем ничего не известно и не должно быть известно. dynamic_cast позволяет не обрушить всю программу потому только, что вы сделали что-то неверно. Ну, например, void func(Base*b) { ((Derived*)b)->derivedfunc(); } Здесь вы вызываете функцию-член, которая есть у Derived, но которой нет в Base. Но что будет, если вы действительно передадите в функцию указтель на Base? Указатель на другого потомка Base, который ничего об этой функции не знает? А dynamic_cast позволяет по крайней мере убедиться, что происходит именно то, что нужно, т.е. что там действительно указатель на Derived. Что до приведения в стиле C - (A*)b - то тут, пожалуй, ближе всего reinterpret_cast - просто рассматривать биты как имеющие иной тип. Без каких-либо проверок. Что уже опасно, но, по крайней мере, в тексте программы в глаза будет бросаться это длинное слово - reinterpret_cast - как указатель на опасность. И это не шутка, это я повторяю Страуструпа - насчет длинных неуклюжих названий ..._cast. P.S. Мне нет нужды напоминать, что при использовании dynamic_cast требуется наличие виртуальных функций, и пояснять, почему?

Ответ 2



Нет ни одной причины использовать запись типа 2. Она появилась как обеспечение совместимости с приведением типов из С и старых версий С++. По нынешним нормам НУЖНО использовать dynamic_cast. Если Вы хотите понять чем отличаются разные варианты cast'ов - дайте знать. Но в данном случае все тривиально. Мы приводим объект класса-потомка к базовому классу Дополнительно обращу внимание, что cast'ы хороши, что они в принципе укладываются в систему обработки исключений и дают человеческие коды ошибок. А использование c-style приведения - это самый простой ( и не лучший!!! ) способ заткнуть компилятор. И замаскировать ошибку, а потом героически пытаться ее найти и/или исправить

Ответ 3



dyanmic_cast нужен для того чтобы безопасно выполнить понижающее приведение(от типа базового класса к типу наследника). Например calss Base{ public: virtual ~Base(){} }; calss A : public Base{}; calss B : public Base{}; void foo(A *a){ Base *base = a; //1 Нормально скомпилируется. //Приведение типа выполнится, но на этапе выполнения //получится какая-то чепуха, причину которой будет //найти ой как непросто B b1* = (B*)base; //2 Полностью идентично 1. //Просто такую запись проще найти при помощи grep //или похожих средств. Так же static_cast не //убирает модификаторы const и volatile, что в //некоторых случая позволит избежать ошибок B b2* = static_cast(base); //3 Также как и случаях 1 и 2 успешно скомпилируется. //Но b3 будет равен 0. dynamic_cast используя RTTI //определил что произошла попытка приведения к //неправильному типу, и вернул 0. B b3* = dynamic_cast(base); //Теперь мы можем написать if(b3 == 0){ std::cerr << "что-то пошло не так\n"; } //И быстро локализовать ошибку } Так же для того чтобы можно было использовать dynamic_cast в базовом классе должна быть хотя бы одна виртуальная функция(деструктор вполне подойдет). Если использовать вместо указателей ссылки, то dyamic_cast будет выбрасывать исключение std::bad_cast, если происходит неправильное приведение

понедельник, 18 февраля 2019 г.

RTTI. Как обратить к статическому полю класса, имея объект Class для него?

Имеем класс Allocator, который имеет метод allocate(Class). Как без знания того, объект Class какого класса пришёл, вытащить из него поле с возможностью его изменить?
Вариант с использованием objOFclassClass.newInstance() не подойдёт. Охота без лишних затрат памяти на создание объекта обойтись.
class X { public static boolean f = false; }
class Y { public static boolean f = false; }
class Z { public static boolean f = false; }
class Allocator { public static void allocate (Class c) throws Exception{ // Предполагая, что (c.name == "X" или "Y" или "Z"), как сделать что то в духе: // c.setStaticField("f",true); ??? } }


Ответ

С помощью метода getDeclaredField получаем объект класса Field Изменяем поле с помощью вызова метода set у этого объекта

Field field = c.getDeclaredField("имя_поля"); field.set(null, true /* новое значение */);
Код на Ideone

понедельник, 26 ноября 2018 г.

Почему обращение к SomeClass.class не вызывает инициализацию класса SomeClass?

Вопрос такой: Почему использование A.class не вызывает инициализацию класса А?
Хотя, учитывая, что подготовка класса к использованию состоит из трёх этапов, на первом из которых создаётся объект Class, и лишь третий из которых -- непосредственная инициализация, вопрос логичнее поставить так Почему вызов Class.forName("test.A"); провоцирует инициализацию класса А? Может сам метод Class.forName(String) создан так, чтобы целиком насильно загружать классы?
package test;
class A { static { System.out.println("A is initialized"); } }
public class Test{ public static void main(String[] args) throws Exception { Class c = A.class; System.out.println("whoa..."); c = Class.forName("test.A"); } }

Expected output: A is initialized whoa...
Real output: whoa... A is initialized


Ответ

В JLS 12.4.1 написано:
Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization.
Что в переводе:
Вызов некоторых отражающих методов в классе Class и в пакете java.lang.reflect также вызывает инициализацию класса или интерфейса.
А в этой документации для метода forName() класса Class чётко написано:
A call to forName("X") causes the class named X to be initialized.
Что в переводе:
Вызов forName («X») вызывает инициализацию класса с именем X.
Соответственно, всё оказалось верно! Метод Class.forName(String) действительно насильно загрузит класс.