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), которая автоматически вставляет код для доступа к закрытым методам. Это может быть полезным.

Другие вопросы по тегам