Java和Python中的继承之间的区别

use*_*790 7 python java inheritance

执行的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()
Run Code Online (Sandbox Code Playgroud)

得到:

1
1
Run Code Online (Sandbox Code Playgroud)

执行类比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;
}
Run Code Online (Sandbox Code Playgroud)

得到:

0
0
Run Code Online (Sandbox Code Playgroud)

显然,从基类继承的Java方法(Test)也使用基类的成员变量.Python方法使用派生类(Subclass)的成员变量.

问题:有没有办法在Java中实现相同或至少类似的行为?

Dao*_*Wen 4

Python 中的对象与 Python 中的字典非常相似。您可以将Test和的每个实例视为一个字典,该字典由您声明的类主体中的代码和赋值Subclass进行更新。__init__您可以想象您编写的代码的工作原理如下:

\n\n
class Test(object):         \n    item = 0                # self[\'item\'] = 0\n\n    def __init__(self):\n        print(self.item)    # print(self[\'item\'])\n\n    def test(self):\n        print(self.item)    # print(self[\'item\'])\n\nclass Subclass(Test):       \n    item = 1                # self[\'item\'] = 1\n\ns = Subclass()              # Test.__init__({})\ns.test()                    \n
Run Code Online (Sandbox Code Playgroud)\n\n

Python 使用鸭子类型,因此item它只是您碰巧拥有实例的任何属性的一些属性。请注意,您实际上不必声明item \xe2\x80\x94,只需分配一个值即可。这就是为什么您能够“覆盖”子类\xe2\x80\x94 中的值,因为您实际上只是覆盖同一字段的旧值。因此,在您给出的示例中,initem实际上Subclass并没有覆盖in ;相反,它们是 Python 对象实例中的相同字段。itemTest

\n\n

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

\n\n

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

\n\n
public class Test {\n\n    private int item;\n\n    public Test() {\n        this(0); // Default to 0\n    }\n\n    public Test(int item) {\n        setItem(item);\n        test();\n    }\n\n    public void test() {\n        System.out.println(getItem());\n    }\n\n    public static void main(String[] args) {\n        Subclass s = new Subclass();\n        s.test();\n    }\n\n    public void setItem(int item) {\n        this.item = item;\n    }    \n\n    public int getItem() {\n        return item;\n    }\n\n}\n\nclass Subclass extends Test {\n\n  public Subclass() {\n      super(1); // Default to 1\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

这看起来像是很多代码,但是一个好的 IDE(例如 Eclipse 或 IntelliJ)会自动为您生成大量代码。我仍然认为它有很多样板,这就是为什么我更喜欢 Scala\xe2\x80\x94,但这是一个完全不同的讨论。

\n\n

编辑:

\n\n

我的帖子太长了,以至于我忘记了为什么要引入 getter 和 setter。重点是,通过封装对字段的访问,您可以做一些更像 Python 中的事情:

\n\n
public class Test {\n   // Same as above . . .\n}\n\nclass Subclass extends Test {\n\n  private int subclassItem = 1;\n\n  public int getItem() {\n    return subclassItem;\n  }\n\n  public void setItem(int item) {\n    this.subclassItem = item;\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,该item字段已被有效覆盖,因为对它的所有访问都是通过 getter 和 setter 完成的,并且这些已被覆盖以指向新字段。但是,这仍然会导致0 1输出而不是1 1您期望的结果。

\n\n

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

\n