Как анонимно создать экземпляр абстрактного класса, хранящегося в объекте класса в 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", поэтому не должно быть никаких оснований ожидать этого.