7

关于在 Java 中访问受保护成员的问题已经被问过很多次并回答了很多次,例如: Java: protected access across packages

但我不明白为什么它是这样实现的,请参阅“Java 编程语言”(第 4 版)的解释:

“限制背后的原因是:每个子类继承超类的契约并以某种方式扩展该契约。假设一个子类,作为其扩展契约的一部分,对超类的受保护成员的值施加约束。如果不同的子类可以访问第一个子类的对象的受保护成员,然后它可以以破坏第一个子类合同的方式操纵它们,这是不允许的。”

好的,这很清楚,但考虑一下这个继承结构(从一些代码中提取):

package package1;

    public class A {
        protected int x;
    }

package package2;

public class B extends A {
    public static void main(String[] args)
        C subclass = new C();
        subclass.x = 7;  // here any constraints can be broken - ??
    }
}

class C extends B {
    // class which places constraints on the value of protected member x
    ...
}

这里 subclass.x = 7 是一个有效的声明,它仍然可以打破 C 的合同。我错过了什么?

编辑(添加):也许我不应该在这种情况下应用引用的逻辑?如果我们只处理一个包,则根本不存在任何限制。所以也许直接继承链以简化的方式处理,这意味着超类必须知道它在做什么......

4

3 回答 3

2

正如您发布的报价中所述,这最终都是关于以下合同的。如果你真的担心有人不会阅读合同,那么有一个防御性编程解决方案可以解决所有这些问题,它引入了修改验证。

我的意思是您发布的代码可能会违反合同;但是,这不能:

public class A {
    private int x;

    protected final void setX(int x) throws IllegalArgumentException {
        if (x < 0)
            throw new IllegalArgumentException("x cannot be negative");
        subValidateX(x);
        this.x = x;
    }

    /**
     * Subclasses that wish to provide extra validation should override this method
     */
    protected void subValidateX(int x) {
        // Intentional no-op
    }
}

在这里,我做了三件大事:

  1. 我将x其设为私有,因此只能从内部分配A(当然,不包括反射之类的东西),
  2. 我制作了 setterfinal来防止子类覆盖它并删除我的验证,并且
  3. 我创建了一个protected可以被子类覆盖的方法,以提供额外的验证,以确保子类可以缩小对 的要求x,但不会扩大它们以包括负整数之类的东西,因为我的验证已经检查过了。

关于如何在 Java 中设计继承有很多很好的资源,尤其是在涉及到超级防御性的合约保护 API 编程时,就像我上面的示例一样。我建议在您最喜欢的搜索引擎上查找它们。

但是,最终,编写子类的开发人员需要有足够的责任阅读文档,尤其是当您进入接口实现时。

于 2013-06-23T04:41:59.607 回答
0

继承的类与其父类是隐含的朋友。所以只要 C 是从 B 继承的,B 对 C 的 x 属性有看法其实是很正常的。

于 2013-06-23T04:33:41.120 回答
0

因为C extends B,有

C c = new C();
c.x = 1;

就您的问题而言,与

B b = new C();
b.x = 1;

Java编译器不考虑上述代码所引用b的对象的运行时类型;c它所看到的只是声明的类型,分别是BC。现在,由于我的第二个示例显然必须有效(毕竟类中的代码B正在访问它自己的属性),因此第一个示例也必须有效;否则这将意味着 Java 允许您在更具体的类型上做更少的事情,这是一个悖论。

于 2013-06-23T16:06:05.500 回答