7

実行された Python コード:

class Test(object):
    item = 0

    def __init__(self):
        print(self.item)

    def test(self):
        print(self.item)

class Subclass(Test):
    item = 1


s = Subclass()
s.test()

与えます:

1
1

類似のJavaコードを実行:

public class Test {
    int item = 0;

    Test(){
        System.out.println(this.item);
    }

    void test(){
        System.out.println(this.item);
    }

    public static void main(String[] args){
        Subclass s = new Subclass();
        s.test();
    }
}

class Subclass extends Test {
    int item = 1;
}

与えます:

0
0

明らかに、基底クラス (Test) から継承された Java メソッドは、基底クラスのメンバー変数も使用します。Python メソッドは、派生クラス (サブクラス) のメンバー変数を使用します。

質問: Java で Python と同じ、または少なくとも同様の動作を実現する方法はありますか?

4

3 に答える 3

4

Pythonのオブジェクトは、Pythonの辞書とほとんど同じです。Testおよびの各インスタンスは、宣言するクラスの本体のコードと割り当てSubclassによって更新されるディクショナリと考えることができます。__init__あなたが書いたコードが次のように機能しているのを想像することができます:

class Test(object):         
    item = 0                # self['item'] = 0

    def __init__(self):
        print(self.item)    # print(self['item'])

    def test(self):
        print(self.item)    # print(self['item'])

class Subclass(Test):       
    item = 1                # self['item'] = 1

s = Subclass()              # Test.__init__({})
s.test()                    

Pythonはダックタイピングを使用itemしているので、インスタンスを持っているものは何でもプロパティの一部にすぎません。実際にアイテムを宣言する必要はなく、値を割り当てるだけであることに注意してください。これが、サブクラスの値を「オーバーライド」できる理由です。実際には、同じフィールドの古い値を上書きしているだけだからです。したがって、あなたが与えた例では、iteminSubclassは実際にはinをオーバーライドしていません。むしろ、それらはPythonオブジェクトインスタンスの同じフィールドです。itemTest

Javaでは、フィールドは実際には特定のクラスに属しています。コードに実際にフィールドの2つの宣言があることに注意してくださいint item。1つはinでTest、もう1つはinSubclassです。を再宣言すると、実際には元のフィールドがシャドウイングint itemされます。一言で言えばJavaを参照してください:3.4.5。詳細については、スーパークラスフィールドのシャドウイングを参照してください。Subclass

あなたがあなたの例で何をしようとしているのか正確にはわかりませんが、これはより慣用的なJavaアプローチです:

public class Test {

    private int item;

    public Test() {
        this(0); // Default to 0
    }

    public Test(int item) {
        setItem(item);
        test();
    }

    public void test() {
        System.out.println(getItem());
    }

    public static void main(String[] args) {
        Subclass s = new Subclass();
        s.test();
    }

    public void setItem(int item) {
        this.item = item;
    }    

    public int getItem() {
        return item;
    }

}

class Subclass extends Test {

  public Subclass() {
      super(1); // Default to 1
  }

}

の値がitem、単純な割り当てではなく、コンストラクター引数を介して設定されていることに注目してください。また、どのようになっているか、それにアクセスするためのゲッターセッターitemのメソッドがあるprivateことに注意してください。これは、よりJavaスタイルのカプセル化です。

これは多くのコードのように見えますが、優れたIDE(EclipseやIntelliJなど)は多くのコードを自動生成します。それでもボイラープレートがたくさんあると思うので、Scalaが好きですが、それはまったく別の議論です。

編集:

私の投稿は非常に長くなり、ゲッターとセッターを紹介したかった理由がわかりませんでした。重要なのは、フィールドへのアクセスをカプセル化することで、Pythonで行っていたようなことを実行できるということです。

public class Test {
   // Same as above . . .
}

class Subclass extends Test {

  private int subclassItem = 1;

  public int getItem() {
    return subclassItem;
  }

  public void setItem(int item) {
    this.subclassItem = item;
  }

}

これで、itemフィールドへのすべてのアクセスがゲッターとセッターを介して行われるため、フィールドは事実上オーバーライドされ、それらは新しいフィールドを指すようにオーバーライドされました。ただし、これでも、期待した0 1ものではなく、出力が生成されます。1 1

この奇妙な動作は、コンストラクター内から印刷しているという事実に起因しています。つまり、オブジェクトは実際にはまだ完全に初期化されていません。thisこれは、構築中にコンストラクターの外部に参照が渡された場合に特に危険です。これは、外部コードが不完全なオブジェクトにアクセスする可能性があるためです。

于 2012-08-18T13:30:39.307 に答える
4

スーパークラスコンストラクターをオーバーロードして、フィールドを0に初期化できitemますTest

public class Test {
    int item = 0;

    Test(){
        System.out.println(this.item);
    }

    Test(int item) {
        this.item = item;
        System.out.println(this.item);
    }


    void test(){
        System.out.println(this.item);
    }

    public static void main(String[] args){
        Subclass s = new Subclass();
        s.test();
    }
}

class Subclass extends Test {

    public Subclass() {
        super(1);
    }
}
于 2012-08-18T13:31:36.157 に答える
3

フィールドを再宣言する代わりに、初期化子を使用します。

public class Test {
   int item = 0;

   ...
}

public class Subclass extends Test {
    {
        item = 1;
    }
}

注:パッケージ構造によっては、itemとして宣言することをお勧めしますprotected

于 2012-08-18T13:27:57.607 に答える