Как анонимно создать экземпляр абстрактного класса, хранящегося в объекте класса в Java?

Если у вас есть абстрактный класс, вы можете создать его экземпляр, получив конкретный анонимный класс. Вот пример:

abstract class A {
     abstract void hello ();
}

A say = new A () { void hello () { System.out.println ("hello"); } }

say.hello(); // -> hello

Как сделать то же самое, если класс хранится в объекте класса? Вот пример:

// -*- compile-command: "javac anon.java && java anon"; -*-

class anon
{
    anon () throws Exception {}

    abstract class AbstractClass
    {
        AbstractClass () throws Exception {}
        abstract void id ();
    }

    AbstractClass x = new AbstractClass ()
        {
            void id () { System.out.println ("X"); }
        };

    Class<AbstractClass> abstractclass 
        = (Class<AbstractClass>)Class.forName ("anon$AbstractClass");

    AbstractClass y = abstractclass.getConstructor().newInstance();

    public static void main (String argv[]) throws Exception
    {
        anon main = new anon();
        main.x.id(); // should print "X"
        main.y.id(); // should print "Y"
    }
}

первый экземпляр (x) работает нормально, но второй (y) терпит неудачу, потому что он пытается создать экземпляр абстрактного класса непосредственно без получения конкретного класса. Как я могу сделать это в Java, имея только объект класса?

4 ответов


у вас может быть недопонимание о том, как именно работают анонимные классы. Анонимный класс на самом деле является обычным классом, как и любой другой, и имеет свой собственный файл класса. Java-the-language предоставляет только некоторый синтаксический сахар над этим и позволяет менее подробный синтаксис для чего-то, что вы можете точно имитировать, объявив обычный именованный класс верхнего уровня в своем собственном файле. Вот почему вы найдете API отражения бесполезным для того, что вы хотите достичь. В принципе, вы хотите динамически создавать класс, у которого нет файла класса. Для этого нужна подходящая библиотека, например javassist.


Если A будет интерфейс вместо абстрактного класса, вы можете сделать это с помощью динамического прокси, но это не работа с абстрактным классом. Пример того, как это работает с интерфейсом:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface A {
    void hello();
}

public class Example {
    public static void main(String[] args) throws Exception {
        @SuppressWarnings("unchecked")
        Class<A> cls = (Class<A>) Class.forName("A");

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println(method.getName());
                return null;
            }
        };

        A instance = (A) Proxy.newProxyInstance(cls.getClassLoader(),
            new Class<?>[] { cls }, handler);

        instance.hello();
    }
}

абстрактные классы не могут быть созданы, поэтому вам действительно нужен новый конкретный класс, который расширяет абстрактный класс. Классы создаются компилятором java из исходного кода. Поэтому напишите исходный код и запустите Java-компилятор. Это нелегко сделать динамически, так как компилятор java требует, чтобы исходный код находился в файле и помещает скомпилированные классы в файловую систему, но возможно. Посмотрите, как это должно делать в создание классов Java динамически . Тогда вы должны загрузить скомпилированные классы-это уже другая история.

Если вы рассматриваете это как "ограничение java", вероятно, вы выбрали неправильный язык для своей задачи (или выбрали неправильную задачу). Попробуйте динамические языки на основе JVM: Groovy, JRuby... их много.


как заявил Марко, анонимный класс такой же, как и любой другой на уровне файла и байтового кода. Это просто синтаксический сахар на уровне языка, который позволяет легко писать небольшие классы.

в вашем примере x.getClass() - это не abstract класса. Это подкласс AbstractClass, который определением id() уже нет abstract. Вероятно, у него есть имя, как anon.

конечно, если бы это было абстрактно, вы не могли бы создать его экземпляр. Это именно то, что вы пытаемся сделать в задании y. Ваше отражение эквивалентно y = anon.AbstractClass(); с переопределением id(). Отражение ошибается во время выполнения так же, как этот оператор будет ошибаться во время компиляции.

вероятно, следующее (В зависимости от существования других анонимных классов и их порядка) и работает без ошибок, но печатает "X":

Class<AbstractClass> abstractclass 
    = (Class<AbstractClass>)Class.forName("anon");  // Note the different class name
AbstractClass y = abstractclass.getConstructor().newInstance();
y.id();  // prints "X", not "Y"

к этому моменту...

main.y.id(); // should print "Y"

нигде в вашем коде у вас нет строки, которая печатает символ" Y", поэтому не должно быть никаких оснований ожидать этого.