2012-11-29 79 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