2013-12-19 6 views
11

私は実際に自分自身で解決できる問題がありましたが、私の元のコードがうまくいかない理由や理解できません。見つかりました。私はここに私のコードの簡略版を提示しています。ジェネリックで「プライベートアクセスがあります」エラー

は、次の抽象スーパーXを考えてみましょう:M1が呼び出されると

public abstract class X{ 

    private int i; 

    public void m1(X x){ 
    x.i = 1; 
    m2(x); 
    } 

    public abstract void m2(X x); 

} 

は、我々は、渡されたインスタンスのXのプライベートフィールドを操作して、私たちはそのインスタンスに平方メートルを呼び出します。

私はXのいくつかのサブクラスを持っています。彼らは、それらが操作するプライベートメンバーも宣言しているという意味で同じです。それを達成するために、彼らは常にm2の冒頭にキャストを行う必要があります。

public class Y extends X{ 

    private int j; 

    public void m2(X x){ 
    Y y = (Y) x; 
    y.j = 0; 
    } 

} 

しかし - 私はXのサブクラスのインスタンスのM1のすべての呼び出しは常に、例えば、同じタイプのものであり、パラメータを持っていることを保証することができます:ここではそのうちの一つであります私がYのインスタンスを持つとき、メソッドm1のパラメータは常にYの別のインスタンスになります。

その保証のため、ジェネリックを導入することでキャストを不要にしたいと考えました。これは私のサブクラスが次のようになるようにするためです:

public class Y extends X<Y>{ 

    private int j; 

    public void m2(Y y){ 
    y.j = 0; 
    } 

} 

スーパークラスXはどのようにして表示されますか?私の最初の試みであることが確認された:

public abstract class X<T extends X<T>>{ 

    private int i; 

    public void m1(T x){ 
    x.i = 1; 
    m2(x); 
    } 

    public abstract void m2(T x); 

} 

しかし - 動作しない、私はこれをコンパイルするとき、私は次のエラーを取得:あなたはプライベートにアクセスしようと何を得る通常だ

X.java:6: error: i has private access in X 

を別のクラスのメンバー。明らかに、Javaは、Tが常にXのインスタンスであることを認識しませんが、宣言で「T extends X」を使用しました。

私はこのようなXを固定:

public abstract class X<T extends X<T>>{ 

    private int i; 

    public void m1(T x){ 
    X<?> y = x; 
    y.i = 1; 
    m2(x); 
    } 

    public abstract void m2(T x); 

} 

少なくとも私は、任意のより多くのキャストを使用していない - しかし、なぜ必要なこの余分な割り当てがありますか?そして、元のコードはなぜ機能しませんでしたか?また、私はX<?>を使用しなければならず、X<T>を使用できなかったことが奇妙なことを発見しました。

答えて

6

私はあなたの質問を以下に減らすことができると信じています:次の例はなぜコンパイルに失敗しますか?

これは本当に驚いています。しかし、我々は実際にこのいずれかが動作しないことに注目することによって式からジェネリック医薬品を削除することができます。つまり

public class Foo { 

    private final String bar = "bar"; 

    public void printFoo(SubFoo baz) { 
     System.out.println(baz.bar); //bar is not visible 
    } 
} 

class SubFoo extends Foo {  
} 

を、問題は、あなたがFooのサブクラスを扱っているということではないFooそのものです。 Tの場合、サブクラスですが、サブクラスであることがわかります。Fooです。

すでに考え出してきたように、(意外にも、少なくとも私には)ソリューションは、アップキャストにある:

System.out.println(((Foo)baz).bar); 

または一般の場合について:

public <T extends Foo> void printFoo(T baz) { 
    System.out.println(((Foo)baz).bar); 
} 

はそうキャストです悪い?あんまり。中間変数を使ったキャストを避けるよりも、確かに良いかそれ以上です。どんなアップキャストと同様、私はそれがコンパイラによって削除されると仮定します。これはコンパイラのヒントとしてのみ存在します。 Tの消去はすでにFooであるため、キャストの安全性を心配する必要はありません。

私は唯一のアクセスについて明確になるように、この制限が必要とされて... SubFoobar自体を再宣言する可能性があるため、それがどのbarが参照されている、ので、キャストが必要である曖昧になる可能性が想定することができます。これは、この複雑な例で実証されています。

public class Foo { 

    private final String bar = "hello"; 


    static class SubFoo extends Foo { 
     private final String bar = "world"; 
    } 

    public <T extends SubFoo> void printFoo(T baz) { 
//  System.out.println(baz.bar); // doesn't compile 
     System.out.println(((Foo)baz).bar); //hello 
     System.out.println(((SubFoo)baz).bar); //world 
    } 

    public static void main(String[] args) { 
     new Foo().printFoo(new SubFoo()); //prints "hello\nworld" 
    } 
} 

これは、キャストよりも多くの修飾子として機能します。

+0

2番目のコードサンプルはコンパイルされませんでしたが、コンパイラは元のコードを批評するのに完全に正しいと思いました。この詳細な説明をありがとうございます! – Bernhard

+0

確かにここでのメッセージは、 'private'がサブクラスからアクセスしたい任意のフィールドに使う誤ったアクセス修飾子であるということでしょうか?代わりにprotected修飾子を使用したり、ゲッターメソッドを提供したりするのはなぜですか? – Bobulous

+0

Arkanon - 私はサブクラスのフィールドにアクセスしていないので、それを非公開にしたいのです。コードをもう一度見ると、宣言しているクラスのプライベートフィールドにアクセスしていることがわかります。さもなければ、それはキャストであってもコンパイルエラーになります! – Bernhard

関連する問題