6

私は Java 8 を使用しています。私の設計では、 や などの値パラメータをモデル化するいくつかの単純なクラスがありFloatParameterますEnumParameter<E>GenericParameter<T>パラメータ名とそのデフォルト値を実装するこれらのクラスの共通のジェネリック スーパー クラス ( ) があります。サブクラスは、の場合の範囲など、サブクラスに固有の他の属性を実装しますFloatParameter

さらに、特定のタイプに関係なく、パラメーターのタイプを操作したいと考えています。しかし、私はまだ、型が のサブ型であるという方法で型をバインドしたいと考えていますGenericParameter<T>。そのために、 などのメソッドを作成しましたprocess(Class<? extends GenericParameter<?>> paramType)

ここで、問題は、型の変数に を割り当てるEnumParameter.class ことができないClass<? extends GenericParameter<?>>一方で、 を割り当てることがFloatParameter.class できないことです。

さらに、クラスのコードをリストして、より明確で再現性を高めます。

public class GenericParameter<T> {
    protected String name;
    protected T defaultValue;
}

public class FloatGenericParameter extends GenericParameter<Float> {
    ...
}

public class TypedGenericParameter<T> extends GenericParameter<T> {
    ...
}

Class<? extends GenericParameter<?>> fgpc = FloatGenericParameter.class; // ok
Class<? extends GenericParameter<?>> tgpc = TypedGenericParameter.class; // error: incompatible types: Class<TypedGenericParameter> cannot be converted to Class<? extends GenericParameter<?>>
Class<? extends GenericParameter> tgpc2 = TypedGenericParameter.class; // warning: [rawtypes] found raw type: GenericParameter

最後に、非ジェネリック基本クラスを使用する場合、問題はありません。

public class Parameter {
    ....
}

public class FloatParameter extends Parameter {
    ...
}

public class TypedParameter<T> extends Parameter {
    ...
}

Class<? extends Parameter> fpc = FloatParameter.class; // ok
Class<? extends Parameter> tpc = TypedParameter.class; // ok

何か提案はありますか?

回避策として使用することも、キャストを行うこともできますがprocess(Class<?> paramType)、コンパイラによる静的な型チェックを利用したかったのです。

編集:

パラメータの型ごとにGUIコンポーネントを生成するファクトリを登録する際にキャストを使いたいと考えています。コードは次のようになります。

addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() { ... })

このような場合、コンパイラはコンパイル時に追加されたパラメーターの型をチェックします。また、コードはより自明になります。

編集2:

現在、提案されたアプローチを使用して、addParameterComponentFactoryメソッドの型パラメーターを導入しています。署名は次のようになります。

public static <P extends GenericParameter<?>> addParameterComponentFactory(Class<P> clazz, ParameterComponentFactory pcf)

この定義により、TypedParameter.class( EnumParameter.class- 型パラメーターも 1 つ) を指定することができ、静的な型チェックを取得できます。

4

1 に答える 1

2

API のコア部分から始めましょう。type の値を持つ名前付きパラメーターを表すジェネリックParameter<T> 型がありますT。特定のタイプのパラメータを編集または表示するように設計された特殊な GUI コンポーネントがあり、ファクトリを登録してこれらのコンポーネントを作成できるようにしたいと考えています。

class Parameter<T> {
    String name;
    T defaultValue;
}

class ParameterComponent<P extends Parameter> {
    void setParameter(final P p) {}
}

interface ParameterComponentFactory<P extends Parameter> {
    ParameterComponent<P> newComponent();
}

class FloatParameter extends Parameter<Float> {}
class FloatParameterComponent extends ParameterComponent<FloatParameter> {}

class EnumParameter extends Parameter<Enum> {}
class EnumParameterComponent extends ParameterComponent<EnumParameter> {}

Parameter私の理解が正しければ、ある型とその型に特化した GUI コンポーネントのファクトリとの間の関係を静的に強制するメソッドを宣言する方法を理解するのに苦労しています 。たとえば、次のように記述できるようにします。

addComponentFactory(EnumParameter.class, EnumParameterComponent::new);    // OK
addComponentFactory(FloatParameter.class, FloatParameterComponent::new);  // OK
addComponentFactory(FloatParameter.class, EnumParameterComponent::new);   // ERROR!

この問題はジェネリック サブタイピングの規則に関連しており、埋め込まれたワイルドカードの代わりに型変数を使用することで回避できます。これにより、厄介なキャストを必要とせずに、必要な型チェックが行われるはずです。

static <P extends Parameter> void addComponentFactory(
    final Class<P> parameterType,
    final ParameterComponentFactory<? extends P> factory) { ... }

説明[1]

P extends Parameter<?>で使用される新しいタイプを導入するClass<P>ことと、直接記述することの違いを説明してくださいClass<? extends Parameter<?>>

これは複雑なので、我慢してください。ワイルドカード、生の型、および変換について少し話しましょう。次の点を考慮してください。

// Scenario 1(a)
GenericParameter raw = /* some value */;
GenericParameter<?> wc = raw;

// Scenario 1(b)
Class raw = GenericParameter.class; 
Class<?> wc = raw;

// Scenario 2
Class<GenericParameter> classOfRaw = GenericParameter.class; 
Class<GenericParameter<?>> classOfWC = classOfRaw;

シナリオ 1(a) と 1(b) はどちらも同じ理由でコンパイルされます。生の型 は、フォームのパラメーター化された型への未チェックの変換Gを受ける可能性があるためです。G<T_1, ..., T_n>

シナリオ 2 はコンパイルされません。しかし、なぜ?

シナリオ 2 では、2 番目の割り当てのどちらの側も raw タイプではありません。割り当てが有効であるため には、恒等変換または 右側の型から左側の型への拡大変換が必要です。参照型の拡大変換の場合、左側の型は右側の型のスーパータイプでなければなりません。これらの型がジェネリックである場合、ジェネリック サブタイピングの規則が 適用されます。具体的には、左側 の型引数には右側の型引数が含まれている必要があります。

Class<String>からへの代入Class<? extends Object>は有効です。 Class<String>は、contains のジェネリック サブClass<? extends Object>タイプ ? extends Object です String。シナリオ 2 では、2 番目の割り当てを有効にGenericParameter<?>するために、 を含める必要がありますが、含まれ GenericParameterていません。 のサブタイプでTはありませんT<?>TスーパータイプですT<?>。したがって、一般的なサブタイプ規則により、 Class<T>は のサブタイプではなくClass<T<?>>、割り当ては有効ではありません。

では、なぜ次のように動作するのでしょうか?

public static <P extends GenericParameter<?>> addParameterComponentFactory(
    Class<P> clazz, 
    ParameterComponentFactory pcf)

addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() {})

上記の呼び出しでは、 on の型推論は完全に引数Pによって駆動され ます。Class<P>を渡しているClass<EnumParameter>のでP 、この場合は生の型にバインドされEnumParameterます。制約 P extends GenericParameter<?>が満たされるためには、GenericParameter<?> から割り当て可能である必要がありEnumParameter、シナリオ 1(a) および 1(b) と同様に、未チェックの変換を介して割り当て可能です。

[1]この説明は露骨な剽窃であり、主にradiodef による他の優れたスタック オーバーフローの回答の融合 です。

于 2017-09-20T01:34:39.840 に答える