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方法使用派生类(子类)的成员变量。

问题:有什么方法可以像在 Python 中一样在 Java 中实现相同或至少相似的行为?

4

3 回答 3

4

Python 中的对象与 Python 中的字典非常相似。Test您可以将and的每个实例视为由您声明的类主体中的代码和分配Subclass更新的 Dictionary 。__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只是你碰巧有一个实例的一些属性。请注意,您实际上不必声明item — 您只需分配一个值。这就是为什么您能够“覆盖”子类中的值的原因——因为您实际上只是覆盖了同一字段的旧值。因此,在您给出的示例中,iteminSubclass实际上并没有覆盖itemin Test;相反,它们是 Python 对象实例中的同一个字段。

在 Java 中,字段实际上属于特定的类。请注意,在您的代码中,您实际上有两个字段声明int item:一个 inTest和一个 in Subclass。当您重新声明int itemin 时,Subclass您实际上是在隐藏原始字段。请参阅Java 简述:3.4.5。阴影超类字段以获取更多信息。

我不确定你想用你的例子做什么,但这是一种更惯用的 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有一个gettersetter方法来访问它。这是更多 Java 风格的封装。

这似乎是很多代码,但一个好的 IDE(例如 Eclipse 或 IntelliJ)会为您自动生成很多代码。不过,我仍然认为这是很多样板,这就是我更喜欢 Scala 的原因——但这是一个完全不同的讨论。

编辑:

我的帖子太长了,以至于我忘记了为什么要介绍 getter 和 setter。关键是通过封装对字段的访问,您可以做一些更像 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字段已被有效地覆盖,因为对其的所有访问都是通过 getter 和 setter 完成的,并且这些已被覆盖以指向新字段。但是,这仍然会导致0 1输出而不是1 1您所期望的。

这种奇怪的行为源于您从构造函数中打印的事实——这意味着对象实际上还没有完全初始化。如果在构造期间将引用传递到构造函数之外,这尤其危险,this因为它可能导致外部代码访问不完整的对象。

于 2012-08-18T13:30:39.307 回答
4

您可以重载超类构造函数以将字段初始化itemTest0:

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;
    }
}

注意:根据您的包结构,您可能希望声明itemprotected.

于 2012-08-18T13:27:57.607 回答