Java和Python继承的区别

2024-04-20 12:23:50 发布

您现在位置:Python中文网/ 问答频道 /正文

执行的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()

给出:

^{pr2}$

执行的模拟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相同或至少相似的行为?在


Tags: 代码testselfdefjavapublicoutitem
3条回答

您可以重载超类构造函数,将Test中的字段item初始化为0:

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

Python中的对象非常类似于Python中的字典。您可以将Test和{}的每个实例看作一个字典,它由您声明的类的主体中的__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使用duck类型,因此item只是您碰巧拥有一个实例的某个属性。请注意,您实际上不必声明项,只需分配一个值。这就是为什么您能够“重写”子类中的值,因为实际上您只是覆盖同一字段的旧值。所以在您给出的示例中,Subclass中的item实际上并不是覆盖Test中的item;相反,它们是Python对象实例中的相同字段。在

在Java中,字段实际上属于特定的类。注意,在代码中,实际上有两个字段int item声明:一个在Test中,一个在Subclass中。当您在Subclass中重新声明int item时,实际上是在对原始字段进行跟踪。有关详细信息,请参见Java in a Nutshell: 3.4.5. Shadowing Superclass Fields。在

我不确定您到底想用您的示例做什么,但这是一种更惯用的Java方法:

^{pr2}$

请注意item的值是如何通过构造函数参数而不是通过简单赋值来设置的。还要注意itemprivate,现在有一个gettersetter方法来访问它。这是更多的Java风格的封装。在

这看起来有很多代码,但是一个好的IDE(比如Eclipse或IntelliJ)会自动为您生成大量代码。我仍然认为这是一个很大的锅炉板,这就是为什么我喜欢Scala,但这是一个完全不同的讨论。在

编辑:

我的帖子太长了,以至于我不知道为什么要介绍getter和setters。关键在于,通过封装对字段的访问,您可以执行类似于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引用传递到构造函数之外,这尤其危险,因为它可能导致外部代码访问不完整的对象。在

使用initializer而不是重新定义字段:

public class Test {
   int item = 0;

   ...
}

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

注意:根据您的包结构,您可能希望将item声明为^{}。在

相关问题 更多 >