クラス<?>がクラスよりも好まれるのはなぜですか
質問
クラスをフィールドとして宣言した場合:
Class fooClass;
Eclipseは私に警告を与えます:
クラスは生のタイプです。汎用型クラスへの参照はパラメーター化する必要があります
これは実際にはどういう意味ですか?そして、なぜ私はそれをするように促されたのですか? Eclipseに「クイックフィックス」を求めた場合、それは私に与えてくれます:
Class<?> fooClass;
それはあまり価値を加えていないようですが、もはや警告を与えません。
編集:なぜクラスはジェネリックなのですか?パラメーター化の例を挙げていただけませんか? <?>
?
編集:うわー!私はこれの深さに気づいていませんでした。私はまた、Java Puzzlerを見ましたが、それは確かにクマのtrapについて私を怖がらせています。だから私はいつも使用します
Class<MyString> myStringClass = MyString.class;
それよりも
Class myStringClass = MyString.class;
(しかし、初日からJavaを使用したので、クラスがジェネリックになったときに本当に気づきませんでした)。
注:これは私にとって理にかなっているので、@oxbow_lakesを受け入れましたが、明らかに非常に複雑な領域です。私はすべてのプログラマーに特定のものを使用するように促します Class<MyString>
それよりも Class
. 。と Class<?>
はるかに安全です Class
.
解決
生の種類と無制限のワイルドカード
以前の答えはどれも、あなたが好むべき理由を本当に扱っていません Class<?>
以上 Class
, 、その表面のように、前者は後者よりも多くの情報を提供していないようです。
その理由は、 生タイプ, 、すなわち Class
, 、コンパイラが一般的なタイプのチェックを行うことを防ぎます。つまり、生のタイプを使用する場合、 君 転覆します タイプシステム. 。例えば:
public void foo(Class<String> c) { System.out.println(c); }
このように呼び出すことができます(両方ともコンパイルされます と 走る):
Class r = Integer.class
foo(r); //THIS IS OK (BUT SHOULDN'T BE)
しかし、そうではありません:
Class<?> w = Integer.class
foo(w); //WILL NOT COMPILE (RIGHTLY SO!)
使用する必要がある場合でも、常にraw以外のフォームを使用することによって ?
タイプパラメーターが何であるか(または境界がある)がわからないため、RAWタイプを使用した場合よりも、コンパイラがプログラムの正しさについて完全に推論できるようにします。
なぜ生タイプがまったくあるのですか?
Java言語仕様 言う:
生のタイプの使用は、レガシーコードの互換性への譲歩としてのみ許可されています
あなたは常にそれらを避けるべきです。固定されていないワイルドカード ?
おそらく他の場所で最もよく説明されていますが、本質的には意味があります 「これは何らかのタイプでパラメーター化されていますが、私はそれが何であるかを知りません(または気にしません)」. 。これは同じではありません 生の種類, 、憎悪であり、ジェネリックを持つ他の言語には存在しません。 スカラ.
クラスがパラメーター化されるのはなぜですか?
さて、ここにユースケースがあります。サービスインターフェイスがあるとします。
public interface FooService
また、システムプロパティを使用して使用するクラスを定義して、その実装を注入したいと思います。
Class<?> c = Class.forName(System.getProperty("foo.service"));
私はこの時点で私のクラスが正しいことを知りません タイプ:
//next line throws ClassCastException if c is not of a compatible type
Class<? extends FooService> f = c.asSubclass(FooService.class);
今、私はインスタンス化することができます FooService
:
FooService s = f.newInstance(); //no cast
他のヒント
それはあまり価値を加えていないようですが、もはや警告を与えません。
あなたが正しい。しかし、これは価値を付加するかもしれません:
Class<FooClass> fooClass;
または、より適切な場合:
Class<? extends FooClass> fooClass;
また
Class<FooInterface> fooClass;
一般的にジェネリックと同様に、指定することでタイプの安全性を向上させることができます なんて 変数を保持したいクラスの。生のタイプに対する警告は、このポテンシャルが使用されないコードの部分をキャッチすることを目的としています。宣言することによって Class<?>
あなたは基本的に「これはそうです 意味した あらゆる種類のクラスを保持する」。
なぜなら、JDK5以来、 Class
パラメーター化されたタイプがあります Class
一般的なオブジェクト。これは(ジェネリックの導入以来)コンパイラがタイプチェックを行う必要があります(もちろんコンパイル時に)。
Class<?>
「不明のクラス」を意味します ?
ジェネリックです ワイルドカード. 。それはそれを意味します fooClass
タイプの Class<?>
aを受け入れます Class
そのタイプは何でも一致します。
典型的な例:
Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.
効果的に、より具体的にパラメーター化されたタイプを提供できます。
Class<ArrayList> = ArrayList.class;
詩 それを念頭に置いてください Class<List> listClass = ArrayList.class;
コンパイルされません(ArrayListはリストのものですが)が、(コメントでMark Petersが述べたように) Class<? extends List> listClass = ArrayList.class;
コンパイルします(ワイルドカードのおかげ)。
のjavadoc クラス クラスは、このクラスにタイプパラメーターが存在する理由についてのアイデアを示しています。
T-これによってモデル化されたクラスのタイプ
Class
物体。たとえば、タイプString.class
はClass<String>.
使用するClass<?>
モデル化されているクラスが不明な場合。
このタイプパラメーターの使用はそれほど明白ではありませんが、クラスのソースコードをざっと見ながら、タイプパラメーターが必要な理由を示します。の実装を検討してください newInstance
方法:
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
}
気付いていない場合、この方法で返されるオブジェクトのタイプは、タイプパラメーターのオブジェクトです。これは、多くの反射を利用するコードで役立ちます。また、適切なタイプのオブジェクトがインスタンス化されていることを確認するために特に注意したい場合があります。
質問の例を考慮して、クラスインスタンスが代わりに次のように宣言された場合
Class<String> fooClass;
Class<Integer> barClass;
String aString;
次に、次のコードをコンパイルすることは次に不可能です。
aString = barClass.newInstance();
要するに、クラスの階層を操作し、厳密なコンパイル時間チェックを課して、コードが多くのことを実行する必要がないことを確認したい場合 instanceof
チェックすると、利用したいクラスのタイプを指定する方がよいでしょう。指定 ?
すべてのタイプを許可しますが、より具体的にする必要がある場合があります。
なぜなら、パラメーター化されたタイプの代わりに生型を使用するには多くの落とし穴があるからです。
その1つは、生のタイプを使用している場合、クラス上のすべてのジェネリックが失われることです。それらの定義された一人一人でさえ。