2012-11-29 83 views
6

如果你有一个抽象类,你可以通过派生一个具体的匿名类来实例化它。这是一个例子:如何匿名实例化存储在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)的失败。我如何在只有Class对象的Java中执行此操作?

+0

等待。你有没有尝试用第一个代码实现第二种方式?我认为它也不会在那里工作。你为什么试图像这样实例化一个抽象类?当你创建一个匿名类时,实际上是扩展你的抽象类并实例化它,而不是抽象类本身。 –

+0

没有y.id()应该打印“Y”。 – ceving

+0

@ceving ..你了解我的评论吗? –

回答

5

您可能会误解匿名类的工作原理。一个匿名类实际上是一个常规类,就像任何其他类一样,并且有它自己的类文件。 Java-the-language只提供了一些语法糖,并且允许使用较少的冗长语法来完成某些你可以通过在自己的文件中声明一个常规命名的顶级类来完全模仿的语法。这就是为什么你会发现Reflection API对你想实现的功能毫无用处的原因。基本上,你想动态地创建一个没有类文件的类。为此,您需要一个合适的库,例如javassist

+0

太糟糕了。无论何时我必须编写Java,我都必须花费大量时间来规避Java的局限性。但是这次墙看起来像一块岩石的脸。 – ceving

+1

确实如此。将Java与岩石进行比较实际上是一种非常有用的比喻,适用于许多不同的情况。我喜欢想象ruby和JavaScript等动态语言的粘土,而不是Java的乐高积木。它们仅以某些狭窄且预定义的方式互锁。 –

+0

嗯,我可以记住我自己的一些乐高积木砖砌成一些东西。必须基因决定我有这个问题。 – ceving

1

如果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(); 
    } 
} 
+0

嗯,听起来像一个有趣的新的Java限制,我还没有意识到:可以用接口完成的东西,但不是抽象类,尽管它们几乎相同。 – ceving

0

抽象类不能被实例化,所以你实际上需要一个扩展抽象类的新的具体类。类由java编译器从源代码生成。所以编写该源代码并运行java编译器。这不是动态的,因为java编译器需要源代码驻留在文件中,并将编译后的类也放入文件系统中,但是可能的。看看它应该怎么做在Generating Java classes dynamically。然后你必须加载编译的类,这是另一回事。

如果你认为这是一个“Java的限制”,也许你chosed错误的语言为你的任务(或chosed错误的任务)。尝试基于JVM的动态语言:Groovy,JRuby ......其中有很多。

+0

你可以自由选择语言吗?你真幸运! – ceving

+0

您可以在任何地方使用java,但只需从java中调用groovy即可完成此任务。 –

0

正如Marko所说的,匿名类在文件和字节代码级别与任何其他类相同。它只是语言级别的语法糖,可以让小类轻松书写。

在你的榜样,x.getClass()不是abstract类。它是AbstractClass的一个子类,它根据id()的定义,不再是abstract。它可能有一个名称,如anon$1

当然,如果它是抽象的,你不能实例化它。这正是你在y的任务中所要做的。您的反思相当于y = anon.AbstractClass();,覆盖id()。反射在运行时出错,就像该语句在编译时会出错一样。

下可能会(视乎其他匿名类的存在,他们的顺序),并运行没有错误,但打印“X”:

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

到这一点......

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

你的代码中没有一行会打印一个“Y”字符,所以不应该有任何理由期待它。

+0

这正是如何写行打印“Y”的问题。 – ceving

+0

没有你的代码在'System.out.println(“Y”);'存在。您的评论(在代码中)无效。 – Anm