Java: доступ к приватному конструктору с параметрами типа
Это продолжение этого вопроса о частных конструкторах Java.
Предположим, у меня есть следующий класс:
class Foo<T>
{
private T arg;
private Foo(T t) {
// private!
this.arg = t;
}
@Override
public String toString() {
return "My argument is: " + arg;
}
}
Как бы я построить new Foo("hello")
используя отражение?
ОТВЕТ
Основываясь на ответе Джталборна, следующие работы:
public class Example {
public static void main(final String[] args) throws Exception {
Constructor<Foo> constructor;
constructor = Foo.class.getDeclaredConstructor(Object.class);
constructor.setAccessible(true);
Foo<String> foo = constructor.newInstance("arg1");
System.out.println(foo);
}
}
6 ответов
Вам нужно получить класс, найти конструктор, который принимает единственный аргумент с нижней границей T (в данном случае Object), заставить конструктор быть доступным (используя setAccessible
метод), и, наконец, вызвать его с нужным аргументом.
Убедитесь, что вы используете getDeclaredConstructors
при получении конструктора и установите его доступность в true, поскольку он является закрытым.
Нечто подобное должно работать.
Constructor<Foo> constructor= (Constructor<Foo>) Foo.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Foo obj = constructor.newInstance("foo");
System.out.println(obj);
Обновить
Если вы хотите использовать getDeclaredConstructor, передайте Object.class в качестве аргумента, который переводится в универсальный T.
Class fooClazz = Class.forName("path.to.package.Foo");
Constructor<Foo> constructor = fooClazz.getDeclaredConstructor(Object.class);
constructor.setAccessible(true);
Foo obj = constructor.newInstance("foo");
System.out.println(obj);
Хорошо, если закрытый конструктор не принимает никаких аргументов, мы получаем проблему при создании нового экземпляра, в этом случае после setAccessible true мы не можем создать объект. Четное construct.newInstance(null);
не будет создавать объект для конструктора аргументов.
Можем ли мы создать объект из кода ниже, используя отражение:
public class Singleton {
private static Singleton instance = new Singleton();
/* private constructor */
private Singleton() {}
public static Singleton getDefaultInstance() {
return instance;
}
}
Да, мы можем создать объект выше класса.
// reflection concept to get constructor of a Singleton class.
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
// change the accessibility of constructor for outside a class object creation.
constructor.setAccessible(true);
// creates object of a class as constructor is accessible now.
Singleton secondOb = constructor.newInstance();
// close the accessibility of a constructor.
constructor.setAccessible(false);
Вы можете сослаться на: Пример 2: "Стремительная инициализация" и "Нарушение Singleton путем отражения" моего блога: http://sanjaymadnani.wordpress.com/2014/04/14/singleton-design-pattern-in-java/
Как сказал @ArtB, вы можете использовать http://dp4j.com/, если вы знаете конструктор, который хотите использовать во время компиляции. На домашней странице проекта есть пример этого, доступ к конструктору Singleton.
Вместо @Test JUnit аннотируйте метод, в котором нужно внедрить Reflection с помощью @Reflect:
public class Example {
@com.dp4j.Reflect
public static void main(final String[] args){
Foo<String> foo = new Foo("hello");
System.out.println(foo);
}
}
Чтобы увидеть сгенерированный код отражения используйте аргумент -Averbose=true, как в этом ответе.
Если тестовый класс Junit (в тестовой папке) имеет то же имя пакета, что и у Actual Class, то из тестового примера Junit мы можем вызвать все приватные методы для тестирования без какой-либо дополнительной библиотеки, такой как dp4j.
Сначала я получал NoSuchMethodException, используя отражение.
Использовать этот:
Constructor<TestClass> constructor= (Constructor<TestClass>) TestClass.class.getDeclaredConstructors()[0];
Существует библиотека для JUnit ( dp4j), которая автоматически вставляет код для доступа к закрытым методам. Это может быть полезным.