26

その名前を使用して、指定されたクラスのインスタンスを作成したいと思います。私のコードを以下に示します。

コンパイラの警告が表示されます。私はこれを正しい方法でやっていますか?クラスの名前を使用して、その型のインスタンスを取得することさえ可能ですか?コンパイラが型がどうあるべきかを知る方法はないと思いますか?

public static <T> T create(final String className) {
    try {
        final Class<?> clazz = Class.forName(className);

        //WARNING: Type safety: Unchecked cast from capture#2-of ? to T
        return (T) create(clazz); 
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public static <T> T create(final Class<T> classToCreate) {
    final Constructor<T> constructor;
    try {
        constructor = classToCreate.getDeclaredConstructor();
        final T result = constructor.newInstance();
        return result;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

ありがとう

4

6 に答える 6

38

最初の方法は次のようになると思います。

public static <T> T create(final String className, Class<T> ifaceClass) 
throws ClassNotFoundException {
    final Class<T> clazz = Class.forName(className).asSubclass(ifaceClass);
    return create(clazz); 
}

タイプパラメータを使用してアップキャストタイプキャストを行うことはできません...それらの厄介なタイプセーフ警告なしでは。

ところで、これらの警告を無視すると、create メソッドは、呼び出し元が使用する実際の型と互換性のないクラスのインスタンスを作成する可能性があります。これにより、後で予期しない ClassCastException が発生する可能性があります。たとえば、インスタンスが割り当てられたとき。


編集: @Pascal は、これをコンパイルするには型キャストを追加する必要があると指摘しています。すなわち

Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass);

残念ながら、@SuppressWarnings アノテーションも追加する必要があります。

于 2009-11-14T09:44:52.843 に答える
2

これは、T に対してパラメーター化されていないためだと思いますClass.forName(..)。Eclipse のオートコンプリートをトリガーすると、clazz.newInstance() が Object を返すと見なされます。したがって、キャストを保持して @SuppressWarnings を追加してください。メソッドを適切に使用しない場合 (つまりString str = Utils.create("java.util.ArrayList");)ClassCastExceptionが発生しますが、それは正常です。

于 2009-11-14T09:50:20.157 に答える
2

2番目の方法は問題ありません。


ただし、最初のクラスでは、任意のクラス名をパラメーターとして渡すことができました。

メソッドのポイントは、呼び出し元のコードがコンパイル時に認識していないクラスをインスタンス化することです。実行時にのみ認識します。

これらの条件では、呼び出し元のコードは型パラメーターを設定できないため、メソッドをパラメーター化できないため、キャストには意味がありません...

したがって、メソッド全体には意味がないと言えます。


呼び出し元のコードがクラスのスーパータイプを認識している場合、メソッドに何らかのポイントを与えることができます。これはパラメーターとして渡すことができ、キャストは可能で意味のあるものになります。

public static <T> T create(final Class<T> superType, final String className) {
  try {
    final Class<?> clazz = Class.forName(className);
    final Object object = clazz.newInstance();
    if (superType.isInstance(object)) {
      return (T)object; // safe cast
    }
    return null; // or other error 
  } // catch and other error code
}
于 2009-11-14T10:23:14.463 に答える
0

上記のコードを動作させることができたとしても、constructor の newInstance() メソッドは、ゼロ引数のコンストラクターを想定しています。

クラスに含まれていない場合、つまり、作成しようとしているクラスのゼロ引数コンストラクターが、メソッドの呼び出し元に応じてプライベートまたはパッケージとして明示的に宣言されている場合、IllegalAccessException が発生します。機能するようになったら、前提条件に追加するもの。

于 2009-11-14T11:10:50.120 に答える
-1

興味深いことがわかりました。ジェネリック型でない場合は、直接Type変換できます。Classしかし、私はまだできませんClass.forName("java.util.List<SomeType>")

import java.lang.reflect.*;
import java.util.*;

public class Main {
    public void func(List<List<String>> listlist, Map<String, List<String>> list, String s) {}

    public static void main(String[] args) throws ClassNotFoundException {
        Method m = Main.class.getDeclaredMethods()[1];

        Type t0 = m.getGenericParameterTypes()[0];
        boolean b0 = t0 instanceof Class;

        boolean b01 = ((ParameterizedType)t0).getRawType() instanceof Class;

        Type t1 = m.getGenericParameterTypes()[2];
        boolean b1 = t1 instanceof Class;
    }
}

ここに画像の説明を入力

于 2016-03-29T13:05:51.330 に答える