2011-12-07 19 views
4

パラメータを汎用メソッドに渡す際に問題があります。 「eventManager.setTarget(この);」Java Genericsメソッドのパラメータ渡しの問題

public class View<T extends View<T,PM>, PM extends Source> { 

    protected PM source; 
    protected EventManager<T, PM> eventManager; 

    public View(PM s){ 
     this.source = s; 
     eventManager = new EventManager<T, PM>(); 
     eventManager.setTarget(this); //error: "The method setTarget(T) in the type 
           //EventManager<T,PM> is not applicable for the arguments (View<T,PM>)" 

     eventManager.setSource(s); 
    } 

    public void setBinding(Topic topic, IEventAction<T,PM> action){ 
     eventManager.setEventAction(topic, action) 
    } 

} 

/** 
* EventManager class has to remain completely generic. The type parameters cannot "extends" 
* anything because the EventManager is used also in other parts where T and S will have to be 
* classes other than "View" and "Source" 
*/ 
public class EventManager<T, S> { 
    protected T target; 
    protected S source; 
    private LinkedHashMap<Topic, IEventAction<T, S>> eventActions; 

    public EventManager(T target, S source){ 
     this.target = target; 
     this.source = source; 
    } 

    public void setTarget(T target){ 
     this.target = target; 
    } 

    public void setSource(S source){ 
     this.source = source; 
    } 

    public void setEventAction(Topic topic, IEventAction<T, S> action) { 
     //some code here ... 
     omissis... 

     eventActions.put(topic, action); 

     omissis... 
    } 

    //other methods down here...omissis 
} 

Eclipseは私が行にコメントを入れ、エラーを与える:コードは次のようです。なぜこのエラーが出るのか理解できません。 とにかく、解決策を見つけました(明らかに)が、私は "きれいな"か "汚い"かどうかはわかりません。この解決策は次のとおりです。

eventManager.setTarget((T)this); 

しかし、「タイプの安全性:ビューからTへのキャストをチェックしない」という警告が表示されます。警告を消去するために、私はコンストラクタメソッドの上に次のものを置く:

@SuppressWarnings("unchecked") 

これはうまくいかず、何が問題なのでしょうか?別の「クリーナー」ソリューション(存在する場合)をお持ちですか?これは「汚い」アプローチだと思いますか?

どんな騒ぎも大歓迎です。

+0

、「これは」コンストラクタからより多くのように、良い習慣ではありませんへの参照をさせる:

一般的に、ここでのアプローチは、クラスが抽象的にし、抽象getThis方法を追加することです並行して文脈で。 – Scorpion

+0

私の答えを読んでも分かりませんが、何の理由もないジェネリックを使用している可能性があります。私はあなたの授業で何をしているのかについて詳しくは分かっていないので、それが本当かどうかは分かりません。 – toto2

+0

@Scorpion 原則として、私は "this"の使用についてあなたが言ったことに絶対に同意します。しかし、私の特別なケースでは、EventManagerへの参照を割り当てるだけで使用されます。 「View」オブジェクトが完全に作成される前に、その参照を使用するものは他にありません。ちなみに、EventManagerを使用する唯一のオブジェクトはViewです。このため、私は安全な状態です。 – user1085876

答えて

3
ラインで

public class View<T extends View<T,PM>, PM extends Source> { 

あなたはTは "thisのタイプ" になりたいように見えます。しかし、それは言語で表現されていません。サイドノートでは

public abstract class View<THIS extends View<THIS,PM>, PM extends Source> { 
    protected abstract THIS getThis(); 
    ... 
     eventManager.setTarget(getThis()); 
     ... 

public final class SomeView extends View<SomeView,SomeSource> { 
    protected SomeView getThis() { 
     return this; 
    } 
    ... 
+0

それはうまくいく!非常に良いアイデア!私はこの解決策が好きです。 Tomさん、ありがとうございました。ありがとうございました。 もちろん、誰かが異なるアイデア/コメント/提案をしている場合は、引き続き議論を続けることができます。 – user1085876

4

エラーは、実行時にViewの任意のサブクラスになることができますが、正確にView(コンパイル時に知られています)を渡しているTでEventManagerをインスタンス化しているために発生します。通常、サブクラスが必要な場合はスーパークラスを渡すことができないため、コードはコンパイルされません。

解決策(コードを変更することなく)はもちろんスーパークラスをサブクラス(あなたがやっていること)にキャストし、ClassCastExceptionを取得しないように最善を尽くしてください。

互換性のない型を渡すことがないと確信できるなら、私は推測しても大丈夫です(非常に混乱しますが)。おそらく何とかそれを再設計しようとします。

1

エラーは、イベントマネージャーが構築中に "T"タイプを保持するように初期化されているが、 "View"(T extends View、スーパークラスを表示する)のインスタンスを割り当てようとしているためです右。

0

私はTudorの言うことに同意するだけでなく、メソッド/コンストラクタのチェックされていない警告を抑制しないことをお勧めします。これにより、その他のチェックされていない例外が隠されます。私はあなたが必要とする行のためにそれをすることをお勧めします。以下はその例です。タイプが同じテンプレート命名することは、混乱を招く可能 -

@SuppressWarnings("unchecked") 
T t = (T)this; 
eventManager.setTarget(t); 
+0

Viewクラスは次のように(実際には抽象クラスである)拡張する必要があります。 パブリッククラスMYVIEWが表示 { 公共MYVIEW(ソースs){ スーパー(複数可)を拡張します。 } \t \t Viewの定義が与えられていると、このように「View 」のようにインスタンス化することはできません。最初のパラメータ型はViewのサブタイプでなければなりません。それ以外の場合、コンパイラはエラーを返します。 だから、 "eventManager.setTarget(this)"という行で、 "this"は常にViewのサブクラスを表し、ランタイムClassCastException(私は思う)を得ることはないと言っています。 – user1085876

+0

@ user1085876コメントにはたくさんのコードがあります。たぶんあなたはそれで答えをつくるべきです。 –

+0

私は試しましたが、できません。私は初心者です。私は8時間前に私自身の疑問に気づくことができません。とにかく、Viewを拡張してスーパーコンストラクタを呼び出すだけのシンプルなサブクラス定義です。すべてのコードを引用符で囲みます。 "パブリッククラスMyViewは、View {public MyView(super)}を継承します。そのために残念。 – user1085876

0

私はラインeventManager = new EventManager<T, PM>();にTがT extends View<T,PM>としてdefindedされているためEventManagerは、ビューを拡張する必要があります宣言を遵守することだと思います。

0

実際に何をしているのか分かりませんが、ここには何かがあります。

ソリューション1

public class View<PM extends Source> { 

    protected PM source; 
    protected EventManager<View<PM>, PM> eventManager; 

    public View(PM s){ 
     this.source = s; 
     eventManager = new EventManager<>(); // diamond notation (Java 7 only) 
     eventManager.setTarget(this); 
     eventManager.setSource(s); 
    } 
}  

あなたはまだViewsetTargetのサブクラスは常に正しい期待のタイプを受け取ることになるかもしれません。

解決方法2:

EventManagerはありません、ソースとしてSource(またはサブクラス)のみ取るべきですか?

public class View { 

    protected Source source; 
    protected EventManager<View> eventManager; 

    public View(Source s){ 
     this.source = s; 
     eventManager = new EventManager<>(); // diamond notation (Java 7 only) 
     eventManager.setTarget(this); 
     eventManager.setSource(s); 
    } 
}  

public class EventManager<T> { 
    protected T target; 
    protected Source source; 

    public void setTarget(T t) ... 
    public void setSource(Source s) ... 
} 

ソリューション3

EventManagerがすべてではパラメータ化されないとViewTargetを拡張または実装しているので、あなたは、Targetクラスまたはインタフェースを持つ必要があります。あなたはジェネリック医薬品を一切持たないままになります。単純な継承ができるのであれば、ジェネリックは必要ありません。

注::Scorpionに記載されているように、コンストラクタでthisをエスケープしないでください。完全に構築される前にViewへのアクセスをトリガーするイベントが発生する可能性があります。

関連する問題