Generics:インスタンスの名前を取得する

I have a method which returns a List.

プロパティは、1つの汎用パラメータを持つ型です。

public class Property> { ... }

混合型のプロパティのリストを持っているので、特定の要素にどの型パラメータがあるのか​​わかりません。

私はそのようなことをしたいと思います:

List list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());
   //                       I know this doesn't exist ----^
}

ある文章では、現在の Property と同じ汎用テンプレート引数を持つために PropertyWrapper が必要です。これを行う方法はありますかですか?

I could apply a suggestion as stated in https://stackoverflow.com/a/3437930/146003 but even if I do this, how to instanciate the appropriate PropertyWrapper then, only having an instance of Class?

I can modify Property<?> if required. I also don't mind if reflection needs to be used (I assume it needs to be)


EDIT: I forgot something. In fact I cannot instanciate the wrapper by the line

PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());

特殊なサブクラス( PropertyWrapper_String )があるためです。

今私は2つの可能性を見ています:

1:文字列でクラスをインスタンス化する:

String strGenericType = "";
Class<?> wrapperClass = Class.forName("PropertyWrapper_" + strGenericType);

2:サブクラスを作成せずに汎用クラスを特殊化する方法はありますか?


あなたのヒントの前に多くのおかげで

5
@JohnB私はマップのアプローチが本当に好きです。実際には、それをたくさん使っています(たとえば、 someclass がキーでコンバータクラスが値です)ストリングからスムーズに変換するコンバータを登録する場合などです。 Pls、あなたの答えにそれを見つけやすくするために追加してください。
追加された 著者 Thomas,
サブクラスを作成せずにジェネリッククラスを特殊化する方法はありますか? - クラスをそのように特殊化することはできません。しかし、インスタンスにはクラス以外の実行時リフレクション情報がないので、インスタンスにジェネリック型クラスを格納する必要があります。すべての回答が既に指摘されています。
追加された 著者 Thomas,
@JohnBはい、それを使うのは意味がありますが、 PropertyWrapper (Class 型)の場合は <> はコンストラクタは、クラスオブジェクトをパラメータとして渡します。
追加された 著者 Thomas,
@ JohnBいいえ、私は彼がジェネリックパラメータのクラス型をコンストラクタに渡したいと思うと思います。彼がリンクしている他の質問とよく似ています。
追加された 著者 Thomas,
はい。私はそれをxxx.classのインスタンスをプロパティクラスに渡してやりました。それは動作しますが、私はそれが醜いと思う。誰かがもっと良い解決策を持っていると思っています。
追加された 著者 Atmocreations,
@ジョンB:あなたは正しいです。ジェネリックパラメータは、存在する場合に getGenericType()が返すものでなければなりません。
追加された 著者 Atmocreations,
キーがラップされているクラス String で、その値がラッパー String である Map < PropertyWrapper_String
追加された 著者 John B,
新しいPropertyWrapper(crt.getGenericType()); を記述したときに、 new PropertyWrapper ();
追加された 著者 John B,
私の答えに更新を掲載しました。
追加された 著者 John B,
しかし、ジェネリック型の PropertyWrapper を作成している場合、コンストラクタは <> 演算子を使用すると思われます。
追加された 著者 John B,
多分あなたはここでいくつかのインスピレーションを見つけることができます: stackoverflow.com/questions/9111899/… からjava型オブジェクトをビルドするにはどうすればいいですか?しかし、解決策が見つからず、OPの本当の目標を達成するための回避策のみが見つかりました。
追加された 著者 Hauke Ingmar Schmidt,

4 答え

次のメソッドを作成してみてください。

 PropertyWrapper createWrapper(Property property){
      return new PropertyWrapper(property);
}

その後、それをそれと呼んでください。

List list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = createWrapper(crt);
}

The reason the above works is that the generic type T is inferred from the argument and is locked down for the entire method. Unlike using <?> in the loop where each instance of <?> is inferred to be a different type.

編集:

ラッパーのクラスに応じて異なるクラス・タイプを持つ問題に対処するには、キーがラップされるクラスであり、値がラッパーであるMapを検討してください。

Map myMap;

そうすれば、次のようなことができます:

Class<?> wrappedType = property.getGenericType();
Class<?> wrapperClass = myMap.get(wrappedType);
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) wrapperClass.newInstance();

引数を渡す必要がある場合は、このようなことを行う必要があります。

Constructor<?> constructor = wrapperClass.getDeclaredConstructor(property.getClass());
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) constructor.newInstance(property);
7
追加された
ありがとう、ジョン。あなたのアイデアはシンプルで、うまくいくかもしれませんが、私はこの方法ではうまくいかないとは思わなかったので、私は質問を修正しました。何か案が?
追加された 著者 Atmocreations,

If you have a Class you can just take that class, create an instance by calling newInstance() on the class object and cast it to T.

The problem you have is getting the generic parameter of crt. If you have concrete subclasses of Property, e.g. StringProperty extends Property, you can get the type using reflection. However, if you only create instances of Property without concrete subclasses and you don't know where the elements of the list are created, AFAIK it is impossible to get the generic type (even if you know where the elements are created it might be impossible though).

したがって、プロパティラッパーにプロパティの型を知らせる唯一の方法は、型パラメーター(クラス)をプロパティ自体に格納することです。ラッパーはその型/クラスメンバー変数のプロパティーを照会できます。

Edit: some explanation on why this is impossible or at least very hard.

The problem with generics is that due to type erasure the generic type is lost when you create a property using new Property(). There's just no runtime information that you could use to retrieve SomeType here.

具体的なサブクラス(具体的なジェネリック型を定義する)がある場合は、各クラスのジェネリックパラメータの実行時に使用できるリフレクション情報があります。次に、各プロパティの実際のクラスを取得し、そのクラスのリフレクションデータを取得できます。

これらの型を定義し、プロパティの参照を返すか保持するメソッドやフィールドがある場合にも、これは可能です。しかし、あなたはいくつかのリストを取得しているようで、そのリストの要素がどこでどのように作成されたのか正確にはわからないので、

さらに、プロパティのクラスはサブクラスではなく Property であると仮定します。したがって、唯一の方法は、ランタイム情報を自分で提供することです。つまり、型クラスへの参照をコンストラクタパラメータとして渡すことです。

1
追加された
@his確認のおかげで:)この質問の1つはこれを行う方法はありますかでした。私の短い答えは次のようになります:いいえ - これはすべての質問に答えなければならない、残りはもう少し説明です:)
追加された 著者 Thomas,
これが評価される理由は何ですか?私に知らせてくれるのは公正なことだ...
追加された 著者 Thomas,
なぜあなたが書いたものが正しいのかわかりません。たぶんあなたは質問に答えなかったので、「あなたが望むのは技術的に不可能です」と言うだけです。実行時に new Property () new Property()と同じであるため、型を取得することも、インスタンスをインスタンス化することもできません。実行時にパラメータ化された型。しかし、私もこの話題で指を焼いてしまった。
追加された 著者 Hauke Ingmar Schmidt,

さて、私は自分自身に答えるつもりです。

I'm now passing an instance of Class<?> to the Property-class.

次に、プロパティの基本的な名前を抽出し、単に "java.lang" を切り捨てます。これは、ほとんどの場合と同じように、私はプリミティブなデータ型 - respに対してこれを行います。彼らのautoboxingクラス。

さらに、ラッパーの新しいインスタンスを名前でインスタンス化し、ラップされたプロパティをパラメータとして渡してそれを適用するコンストラクターに渡します。

あなたの中に興味のあるもののためのコードです:

String template = "...";//some package definition
for (Property<?> crt : bag)
{
    String className = template + crt.getClassName();
    Class<? extends PropertyWrapper<?>> wrapperClass = null;
    wrapperClass = (Class<? extends PropertyWrapper<?>>) Class.forName(className);
    Constructor<? extends PropertyWrapper<?>> constructor = wrapperClass.getConstructor(new Class<?>[] {Property.class});
    PropertyWrapper<?> wrapper = constructor.newInstance(crt);
   //Further operations using the wrapper
}

簡単にするために、私はエラー処理の部分を省いた。

0
追加された
これにはさらに、各プロパティータイプ、つまり文字列ラッパー、整数ラッパーなどのラッパークラスが必要です。これは、 PropertyWrapper のみを持つ質問と矛盾するようです。 - Btw、私はまだそれらのラッパーが必要なのか分からない。
追加された 著者 Thomas,
うーん、私はその解決策が気に入らない。それは本当にリファクタリングが安全ではないでしょう(例えば、パッケージを変更するときにテンプレートを変更するのを忘れるかもしれません)。そして文字列を手に入れることはやや壊れやすいでしょう(そして、ラッパーがプロパティと同じ単純な名前を持っているかのようです彼ら?) @ JohnBがコメント内で提案したマップアプローチを使用し、 String.class StringWrapper.class などにマッピングするのはなぜですか?これにより、文字列を操作してリファクタリングの安全性を向上させる必要がなくなります。
追加された 著者 Thomas,

コールサイトからラッパーをインスタンス化するのではなく、独自のラッパーを作成する方法を Property で知っているのはなぜですか?何かのようなもの:

public class Property> {
    PropertyWrapper newWrapper();
}

しかし、半分しか役立たない。あなたの本当の問題はあなたのようなループの中です:

List list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = crt.newWrapper();
}

PropertyWrapperとPropertyが "同じ"型を持っているという事実はあまり有益ではありません。なぜならその型は単なるワイルドカードであるからです。

I really hate to give one of those "what are you really trying to do" answers, but -- what are you really trying to do? Generally speaking, once you get to an unbound wildcard, all hope is lost (unless you're willing to do unsafe, uncheckable casts or bend over backwards with reflection checks). One way around this is to put as much of the action within the Property as possible,before you put that property in the List. My newWrapper() is an example of this: it puts the action of creating a PropertyWrapper into the Property itself. If you wanted to register a callback for when the property changed, that's also something you may be able to do at each place you instantiate a non-wildcard Property. For instance:

Property property = new Property();
SomeListener listener = whatever();
property.addListener(listener);
wildcardedPropertiesList.add(property);

この特定の例はおそらく役に立たないでしょうが、うまくいけばそれはあなたに適用可能なアイデアを与えるでしょう。

0
追加された