接口保护

为什么 interface定义中的所有方法都是隐式的 public? 为什么它不允许 protected方法?

69572 次浏览

因为接口应该表示“从类外部可以看到的内容”。添加非公共方法是没有意义的。

因为接口定义了公共 API。任何受保护的都是内部细节,不属于接口。

您可以使用带有受保护的抽象方法的抽象类,但是接口仅限于公共方法和公共静态 final 字段。

也许,因为它是一个 接口,也就是说,它是用来告诉客户端他们可以用实例做什么,而不是告诉他们他们不能做什么。

唯一有意义的场景是当您希望将可见性限制到同一个包时。protected的所有其他用途都不适用。具体来说,protected方法通常用于为后代提供对较低级别实现的某些细节的访问。但是,在接口中声明这一点是没有意义的,因为没有更低级别的实现可以公开。

甚至包场景也不是接口的真正内容。

要实现您可能想要的目标,您需要两个接口,一个用于内部使用,另一个用于公共 API 中公开。(可能是内部的,但不一定是公共的。)或者,正如其他人指出的,一个抽象超类。

因为实现类必须实现接口中声明的所有方法,所以如果实现类位于不同的包中,会发生什么情况?

接口如果您希望使用您所描述的内容,可以继续使用抽象类或嵌套接口。

关于接口变量的一部分,但仍然适用于方法:

接口变量是隐式公共的,因为接口旨在提供一个应用程序编程接口(API) ,Java 程序员可以完全访问这个 API,以便在自己的应用程序中引用和实现它。由于接口可能用于不同于其自身的 Java 包中,因此公共可见性确保程序代码可以访问该变量。

虽然经常被引用的原因是“接口定义公共 API”,但我认为这是过于简单化了。(而且它“闻起来”也像是循环逻辑。)

具有混合访问修饰符的接口并不是没有意义的; 例如,部分为公共的,部分限制于与接口在同一个包中的其他类。事实上,在某些情况下,这可能是非常有用的,IMO。

实际上,我认为 推理隐式公开接口成员的部分原因在于 它使 Java 语言更简单:

  • 隐式公共接口成员对于程序员来说更容易处理。有多少次您看到代码(类)中的方法访问修饰符似乎是随机选择的?许多“普通”程序员很难理解如何最好地管理 Java 抽象边界 1。向接口添加 public/protected/package-private 会使它们更加困难。

  • 隐式公共接口成员简化了语言规范... ... 因此也简化了 Java 编译器编写者和实现反射 API 的人员的任务。

“接口定义公共 API”的思路可以说是简化语言设计决策的结果(或特征) ... 而不是相反。但实际上,这两种思路可能在 Java 设计人员的头脑中并行发展。

无论如何,官方对 JDK-8179193中 RFE 的回应清楚地表明,Java 设计团队决定 2允许在接口上使用 protected会增加复杂性,但实际收益很小。向 找到证据的@skomisa 致敬。

RFE 中的证据解决了这个问题,这就是为什么没有添加这个的官方原因。


当然,一流的程序员在这些事情上没有困难,并且可能欢迎更丰富的访问控制特性。但是,当他们的代码交给其他人来维护时会发生什么呢?

你可能不同意他们的决定或他们陈述的理由,但这是没有意义的。

我不得不说,这个问题已经被 Java8中缺省方法的引入重新打开了。我现在正在进行的项目,类似于接口的基本特性,意味着从实现中抽象意图。

有几种情况下,我可以用“默认保护”方法彻底简化我的代码。事实证明,这并不实际工作,因为接口仍然坚持 Java7逻辑。由于上面提到的原因,一个普通的受保护的方法没有任何意义; 但是如果一个默认的公共方法需要一个不太可能改变的低级资源,并且可以由受保护的方法提供,在我看来,拥有“默认受保护”的工作不仅可以维护更清晰的代码,还可以保护未来的用户免受意外的滥用。

(可悲的是,这并不能改变这样一个事实,即我仍然需要用其他不必要的摘要来过度复杂化我的代码; 但是我确实打算向 Oracle 提出一个特性请求。)

很强烈认为接口应该允许受保护的方法; 谁说接口必须对全世界的每个人都是可见的?至于你的观点,它可能会混淆“普通”(读: 不称职)程序员: 如此多的面向对象是关于正确的结构化对象,类,包等,如果一个程序员有一个困难时间做所有这些正确,他有一个更大的问题。Java 就是 建造

声明内部子接口是一种很好的做法,但是从技术上讲,在 Java 接口中不能将内部方法声明为 protected

当然,您可以为内部使用创建另一个扩展公共接口的接口:

package yourpackage;


public interface PublicInterface {


public void doThing1();


public void doThing2();


public void doThing3();


}

package yourpackage;


interface InternalInterface extends PublicInterface {


void doAnyInternalThing1();


void doAnyInternalThing2();


}

您可以在包中使用 InternalInterface接口,但是您应该接受 PublicInterface的任何子类型(在公共方法中) :

package yourpackage;


public class SomeClass {


public void someMethod(PublicInterface param) {
if (param instanceof InternalInterface) {
// run the optimized code
} else {
// run the general code
}
}


}

外包用户可以毫无问题地使用 PublicInterface

通常程序员在类似的情况下创建抽象类,然而,在这种情况下,我们失去了多重继承的好处。

只有当子类扩展了基类时,子类才能访问受保护的方法。

在接口的情况下,子类从不扩展接口,而是实现接口。

受保护的方法可以通过 延伸而不是通过 执行访问。

接口指向 把方法暴露给外部世界。因此,这些方法本质上是公开的。但是,如果您想引入 同一类中的抽象,可以在接口和实现类之间创建另一个抽象级别,即抽象类。下面演示一个示例。

public interface MyInterface {
public void publicMethod(); // needs to be public
}


public abstract class MyAbstractClass implements MyInterface {
@Override
public void publicMethod() {
protectedMethod(); // you can call protected method here
// do other stuff
}
protected abstract void protectedMethod(); // can be protected
}


public class MyClass extends MyAbstractClass {
@Override
protected void protectedMethod() {
// implement protected method here, without exposing it as public
}
}

这里的一些答案使用循环推理来解释为什么接口方法不能被保护: 因为它们必须是公共的,所以显然它们不能被保护!

这解释不了任何问题,但幸运的是,有人提出了在接口中使用受保护方法的增强请求,作为一个 JDK bug几年前就解释了这个问题:

接口中的受保护方法: 跨包共享

由于修饰符在 Java 中有一些限制,这是一种共享方法的方法 跨包的方法仅限于公共方法 将方法公开是危险的,但它需要是因为 缺乏适当的修饰。我的解决方案克服了这个限制。

Java 语言规范目前不允许受保护的 接口方法的修饰符。我们可以利用这个事实和 使用这个新特性的保护接口方法。

如果接口方法标记为 protected,而该接口为 由另一个包中的类实现,则该方法不需要 可以是公共的,但也可以是私有的,或者至少是包保护的。 该方法是可见的,不管类声明它是什么和 在接口的源代码包中还可以看到(和 sub 包裹?)。

通过这种方式,我们可以在众所周知的包之间共享某些方法。

这是对那个增强请求的响应,它以状态 Won't fix关闭:

这个建议试图用一种新的方法来解决问题 复杂性和特殊情况下,几乎没有实际收益。一个典型的方法 解决这个问题的方法是使用一个私有类实现一个公共 实现方法是公共的,但是位于 私人班级,所以他们保持私人。

从 Java9开始的一个替代方法是创建类和 方法,但是在具有限定导出到 特定的“朋友”模块,而不是被导出到一般 公众人士。

因此,从这份错误报告中得出的权威结论是:

  • 当前的情况不会改变; 接口不太可能支持 protected方法。
  • 在接口中不支持 protected方法的理由是它是“ 增加了复杂性和特殊情况,但几乎没有实际收益”。
  • 从 Java9开始,有一种替代方法可以提供对方法的包级访问。使用 Java 平台模块系统到“ 将类和方法公开,但是在具有限定导出到特定“朋友”模块的模块中,而不是导出到一般公共模块中”。

public成员通常由 public成员使用。例如,AbstractList.clear()使用 AbstractList.removeRange(int, int),重写它将提高 clear的性能。

如果接口中允许使用 protected方法,这意味着大多数 public default实现将依赖于这些方法。如果子类不需要 default实现并覆盖所有 public方法,那么所有非 public方法将不再有用。如果它们是 abstract,我们仍然必须覆盖它们,这使得子类变得复杂。

我知道这是非常晚/老,我肯定,现在你有很多比我更多的经验,因此,如果这有助于任何人,干杯,..。

我已经找到了一种方法,保持舒适的内聚力,防止使用对象时,处理受保护的包。

事后看来,这似乎简单得可笑,但事实是,无论你想用一段代码实现什么目标,如果你花费任何时间尝试使其适应标准和/或调整代码的可重用性,那么实现这个目标就会变得十倍困难。

现实是,秩序,将展开,因为你去. 。

但是我做了一个图表,因为它帮助我在一些事情上,取决于问题可以很容易地弯曲我的头脑。

enter image description here

public interface CommonPublics {
void publicInAll();
}
public static abstract class CommonProtecteds implements CommonPublics {
protected abstract void protectedInAll();
}
public interface MyObjectPublics {
void myObjectPublic();
}
public static class MyObject extends CommonProtecteds implements MyObjectPublics {


@Override
public void publicInAll() {


}


@Override
protected void protectedInAll() {


}


@Override
public void myObjectPublic() {


}
}

现在,当您需要在同一个包中处理 protected 时,不需要处理 MyObject,而是可以处理 CommonProtected。

现在,理论上,你可以把处理 protected 的任何逻辑放在抽象中,这取决于你,但是如果出于某种原因,你正在处理并发,最好是尽可能紧密和接近同步主体。因此,无论受保护者需要处理什么,可能需要在不同的锁定位置,最好放在将处理它的对象中。