2011-02-01 15 views
21

文字列の値でクラスをインスタンス化します。私はこれを行うためのいくつかの方法を示すいくつかのチュートリアルを見つけました。クラスはあるインタフェースから継承しなければならない、という特別なメソッドを持っているImplementMe。だからここで私が試したものです:文字列からクラスをロードする

ImplmentMe a = 
    (ImplementMe) ImplementMe.class 
        .getClassLoader() 
        .loadClass("my.package.IImplementedYou") 
        .newInstance(); 
a.runMe(); 

それは動作しますが、それはとても醜いです。私は少なくともキャストを必要としないと予想していました。より良い方法があると教えてください。

+2

キャストに関係なく必要です。コンパイラは 'Object'を自動的に' ImplementMe'に昇格することはできません。 –

+0

それはとても奇妙です。私は 'ImplementMe.class.getClassLoader()'を使って、クラスが 'ImplementMe'から継承しなければならないというヒントを得ました。 – User1

+0

'Class'には' asSubclass'メソッドがあります。 '... loadClass(...)。asSubclass(ImplementMe.class).newInstance()'は、(成功した場合) 'ImplementMe'インスタンスを返します。 – user2478398

答えて

9

お試しClass.forName("my.package.IImplementedYou")

+4

彼はまだキャストが必要であることに気付くかもしれません。 –

2

サードパーティのツールキットを使用しているかどうかにかかわらず、すべての本質的なことがあります。 Objectを予期しない限り、オブジェクトのキャストは本質的にはでなければなりません。

使用できる
public <T> T instantiateObject(String name, Class<T> cls) throws Exception { 
    return (T) Class.forName(name).newInstance(); 
} 

:あなたは、しかし、あなたのためにあることない日常作ることができます

AClass cls = instantiateObject("com.class.AClass", AClass.class); 

をしかし、あなたはここまで来れば、String nameはAClassは、具体的なクラスである与えられた(実際には冗長です)。あなたにも可能性があります:

あなたが使用することができます
public <T> T instantiateObject(Class<T> cls) throws Exception { 
    return (T) Class.forName(cls.getCanonicalName()).newInstance(); 
} 

AClass cls = instantiateObject(AClass.class); 
+0

'(T)Class.forName(cls.getCanonicalName())。newInstance();'なぜそれほど複雑なのですか?どうすれば 'cls.newInstance();'?しかし私の解釈では、インターフェイスクラスはロードされていますが、実装クラスではありません。 –

+0

真、 'cls.newInstance()'はもっと短くなります。また、第2のアプローチを使用すると、具体的なクラスでなければなりません。 –

1

あなたは少し

ImplementMe a = (ImplementMe) Class 
           .forName("my.package.IImplementedYou") 
           .newInstance(); 

のようにそれを短縮することができますが、キャストを取り除くことはできません。キャストを避ける方法があるかもしれませんが、名前でクラスをロードするという副次的な問題を避けることができる場合のみです。

30

いいえ、いい方法はありません(設計通り)。あなたはこれをやらなければなりません。Javaはタイプセーフな言語として設計されています。しかし、私はあなたが時々、このようなことを行う必要があることを理解することができ、その目的のために、あなたは、このようなライブラリ関数を作成することができます

public <T> T instantiate(final String className, final Class<T> type){ 
    try{ 
     return type.cast(Class.forName(className).newInstance()); 
    } catch(InstantiationException 
      | IllegalAccessException 
      | ClassNotFoundException e){ 
     throw new IllegalStateException(e); 
    } 
} 

今すぐあなたのクライアントコードは、少なくともキャストせずにこのメソッドを呼び出すことができます。

MyInterface thingy = 
    instantiate("com.foo.bar.MyInterfaceImpl", MyInterface.class); 
+1

未チェックのキャストは必要ありません。 'return type.cast(Class.forName(className).newInstance());'を使用してください。利点は、それがすぐにスローされることです(フェイル・スピードが増します)。 – maaartinus

1

代替forNameを使用することですが、それはあなたが現在持っているものよりもはるかに良くなることはありません。確かに

ImplementMe a = 
    (ImplementMe) Class.forName("my.package.IImplementedYou").newInstance(); 
a.runMe(); 

forNameは、source code,Class.javaのように、クラスをロードするためにシーンの裏にgetClassLoader().loadClass()を使用します。

0

コンパイラはオブジェクトがImplementMe型であることをコードから認識できないため、キャストが必要です。したがって、プログラマはキャストを発行する必要があります。このキャストは、オブジェクトがImplementMeインスタンスでない場合にClassCastExceptionをスローします。

0

あなたが持っていることはありますが、ImplementMeをロードした同じクラスローダーを使用してクラスをロードする必要はありません。これは、同じようにうまく動作するはずです:

Object newInstance = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou").newInstance(); 

重要なことは、クラスローダは「my.package.IImplementedYou」の実施や「ImplementMe」の実装を持つクラスとクラスファイルの両方を知っていることです。

を明示的IImplementedYouは本当にこのようImplementMeを実装していることを確認することがあります。

if(newInstance instanceof my.package.IImplementedYou) { 
    ((ImplementMe)newInstance).runMe(); 
} 

あなたはまたIImlementedYouが実際にインスタンスを作成する前に、インターフェイスを実装していることを確認することがあります。どのように私は希望

Class c = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou"); 
if(ImplementMe.class.isAssignableFrom(c)) { 
    Object newInstance = c.newInstance(); 
} 
5

はここですする:

ImplementMe noCastNeeded = 
    this.getClassLoader() 
     .loadClass("my.package.IImplementedYou") 
     .asSubclass(ImplementMe.class).newInstance(); 

いくつかの例外がありますそれは大丈夫だと思います。 :)

関連する問題