受接口保护


111

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


22
很好的问题。对于Java中的几乎所有其他内容,我已经找到做出选择的真正原因,但是对于这一点我没有。对我而言,在接口中定义一个受保护的方法非常有意义,该方法允许同一包内的另一个类在实现对象上使用此方法而无需公开该方法,除了包成员,以前往世界其他地区。
Markus A.

4
@MarkusA。但是接口是双向的,即它们也可以由当前程序包外部的类实现(然后可以作为参数传递给此程序包内部的方法)。当前程序包外部的类将如何实现某些公共接口的“受保护”方法?
MartinStettner

8
@MartinStettner:不会。这就是重点。一个包可能具有多个不相关的类,这些类实现了一个接口,并希望保证接收该接口类型的引用的任何代码都将以某种方式运行。如果可以防止外部代码声称以违背其约定的方式表现接口,则可以使这种保证更加强大。
supercat

1
@MarkusA。您提出了一个好观点,您应该可以使用Java 9的模块系统
Nir Alfasi 2016年

1
如果interface具有protectedmethod,则所有实现类都将被视为接口的子类型。所有这些类都可以访问受保护的方法。它会使protected方法上的关键字无效吗?只要我们没有任何方法来限制谁实现此接口 ,方法上的protected关键字是无用的。如我错了请纠正我!
amarnath harish

Answers:


67

因为接口应该表示“您可以在课堂外看到的东西”。添加非公共方法是没有道理的。


16
但是,为什么不具有仅与接口相同的程序包成员“可以从类外部看到”的函数呢?我有几个我希望这样做的用例。
Markus A.

5
@MarkusA。我意识到这是晚了,但你可以做一个全面abstract class,这是所有interfaces为,并指定你想要的任何访问。这样做会失去interfaceJava中实现的多个实现的好处,但是老实地建立一个遵守其他程序包限制的合同将是不可测试的并且令人困惑,因为您实际上无法在该程序包之外访问自己的实现方法。
pickypg

10
@pickypg但是,如果要实现该接口的类已经扩展了另一个类,则无法使其扩展另一个类。对于仅在包内使用的接口,我不会感到困惑。
Flamma 2014年

24
@ Raveline,-1,这引出了一个问题:“为什么接口应该表示您可以从课堂之外看到的内容?” Java 8已经在接口中允许方法主体,那么为什么不也允许受保护的抽象方法呢?
Pacerier,2014年

8
我经常读这种解释,但是恕我直言是错误的。无论是谈论OOP,通信API还是硬件,接口都是一种“标准交换协议”。我PC上的USB端口显然是一个公共接口。但是我主板上的钥匙锁外壳后面的可访问可选USB端口的引脚显然是“受保护的”接口。然后我们有了BIOS芯片-这也是一个标准化的接口,但是它从来没有以任何方式公开,只有很少的公司秘密知道确切的细节。因此,接口当然可以具有任何可见性!为什么不在OOP中?
Foo Bar

55

尽管经常引用的原因是“接口定义了公共API”,但我认为这过于简单了。(它也“闻”到循环逻辑。)

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

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

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

  • 隐式地,公共接口成员简化了语言规范,从而简化了Java编译器作者和实现Reflection API的人员的工作。

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

无论如何,对JDK-8179193中 RFE的官方回应清楚地表明,Java设计团队决定2允许protected接口增加了复杂性,却没有什么实际好处。感谢@skomisa 查找证据

RFE中的证据解决了这个问题。这是为什么没有添加的正式原因。


1-当然,顶尖的程序员在处理这些事情上没有困难,并且可能会欢迎使用更丰富的访问控制功能。但是,当他们的代码移交给其他人维护时会发生什么呢?

2-您可能不同意他们的决定或他们陈述的理由,但是那是没有根据的。


21

我必须说,通过在Java 8中引入默认方法,这个问题已经重新出现。我现在正在从事的项目类似于接口的基本性质,旨在从实现中抽象意图。

在几种情况下,我可以使用“默认保护”方法彻底简化代码。事实证明,这实际上不起作用,因为接口仍然遵循Java 7逻辑。由于上述原因,普通的受保护方法没有任何意义。但是,如果默认的公共方法需要不会更改的低级资源并且可以由受保护的方法提供,那么在我看来,拥有“默认受保护”的工作不仅会维护更干净的代码,而且还会保护未来的用户意外虐待。

(这可悲地改变了我仍然需要用其他不必要的摘要来使我的代码过于复杂的事实;但是我确实打算在Oracle中提出功能请求。)


我同意100%。在引入默认方法之前,抽象类是明智的选择。但是,它们对多重继承的限制意味着它们并不完美。使用默认方法的接口通常在> = JDK 1.8世界中是更好的替代方法,但是由于它们无法存储状态,因此它们依赖于定义其他抽象方法来公开状态,这意味着该状态是公开的,并不总是这样。你要。
Jeremy Krieg神父

10

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

您可以将抽象类与受保护的抽象方法一起使用,但接口仅限于公共方法和公共静态最终字段。


5
您说过“因为接口定义了公共API”。那么,接口仅应定义publicAPI 的原因是什么?protected在Java中,“内部细节”与“实现细节”之间是有区别的,因为Java现在绝对是一个内部接口,它公开给所有可以继承它的人,而这个接口基本上是整个世界。
Pacerier 2014年

1
Java 8的默认方法不再适用。
马里奥·罗西

@MarioRossi Java 9的私有接口方法也不再适用。
Skomisa

7

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


1
我不明白为什么在不将实现暴露给外界的情况下,接口也不能告诉子类它们可以做什么。这是在抽象类可以用作完美替代方案时做出的设计决策。但是随着接口中默认方法的出现,抽象类现在已成为不完善的替代方法。
杰里米·克里神父

6

强烈认为接口应允许使用受保护的方法。谁说界面必须对全世界所有人可见?关于您的观点,这可能会使“普通”(不称职的)程序员感到困惑:太多的OOP都是关于正确构造对象,类,包等的,如果程序员在完成所有这些事情时遇到困难,他有一个更大的问题。Java是为这种类型的东西而构建的。


这不能回答问题。
Stephen C

5

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

这没有任何解释,但是幸运的是,几年前有人提出了对接口中受保护方法的增强请求,这是JDK的错误,它为您揭示了这个问题:

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

由于Java中的修饰符有些限制,因此跨包共享方法的方法仅限于公共方法。有时将方法公开是很危险的,但这必须是因为缺少适当的修饰符。我的解决方案克服了这一限制。

Java语言规范当前不允许接口方法使用protected修饰符。我们可以利用这一事实,并为该新功能将protected用于接口方法。

如果接口方法被标记为受保护并且该接口是由另一个程序包中的类实现的,则该方法将不必是公共的,但也可以是私有的或至少受程序包保护。该方法是可见的,无论该类声明了什么,并且在接口的源包(和子包?)中还可见。

这样,我们可以在知名软件包之间共享某些方法。

这是对该增强请求的响应,该请求已通过status关闭Won't fix

该建议试图以增加实际复杂度和特殊情况的方式解决问题。解决此问题的一种典型方法是拥有一个实现公共接口的私有类。实现方法是公共的,但在私有类中,因此它们保持私有。

从Java 9开始可用的替代方法是将类和方法公开,但在一个模块中,该模块具有合格导出到特定“朋友”模块的功能,而不是导出到普通大众的模块。

因此,该错误报告的权威之处在于:

  • 目前的情况不会改变。接口不太可能支持protected方法。
  • protected在接口中不支持方法的理由是,它“ 增加了复杂性和特殊情况,却很少获得实际收益 ”。
  • 从Java 9开始,提供了对方法的包级访问的替代方法。使用Java平台模块系统(JPMS)将类和方法公开,但在具有合格导出到特定“朋友”模块而不是导出到普通公众的模块内 ”。

4

由于实现类必须实现接口中声明的所有方法,因此,如果实现类位于其他程序包中会发生什么呢?


2

接口如果要使用您所描述的内容,请继续使用抽象类或嵌套接口。

代码风格摘录的有关接口变量的内容,但仍适用于方法:

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

2

声明内部子接口是一个好习惯,但是从protected技术上讲,您不能像在Java接口中那样声明内部方法。

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

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毫无问题地使用。

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


1
使用者也可以使用外包装YourPublicInterface.Internal。接口中的所有内容(包括嵌套接口)都是公共的,而与public关键字的存在与否无关。
格雷格·罗洛夫斯

1

唯一有意义的情况是您想限制对同一程序包的可见性。的所有其他用途protected均不适用。具体而言,protected通常使用方法为后代提供对较低级别实现的某些详细信息的访问。但是在接口中声明是没有意义的,因为没有要公开的较低级别的实现。

甚至包方案也不是真正的接口。

要实现您可能想要的功能,您需要两个接口,一个接口供内部使用,一个接口在公共API中公开。(内部有可能,但不一定扩展公共的。)或者,正如其他人指出的那样,抽象超类。


抽象超类可以防止自身由其包外部的类型派生。可以确信接收此类超类类型的引用的人信任该包的作者,可以确保该对象将按声明的方式运行。如果一个包有多个类想要公开某些通用功能,但不适合一个适当的层次结构(例如,一个实现函数X和Y,一个Y和Z以及一个X和Z),则它可能会有所帮助使用接口的功能,同时仍然保证接口类型所引用的实例将是“真正的”。
supercat

0

仅当子类扩展了基类时,子类始终可以访问受保护的方法。

对于接口,子类从不扩展接口。它实现了接口。

受保护的方法可以通过extend访问,而不能通过Implement访问


什么关键字有什么区别,没关系。
Alex78191

0

接口旨在将方法公开给外部世界。因此,这些方法本质上是公开的。但是,如果要在同一个类家族中引入抽象,则可以通过在接口和实现类之间创建另一个抽象级别(即抽象类)来实现。下面演示一个示例。

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

2
但是接口中没有人可以看到的私有方法。
Alex78191

Java在接口中具有默认方法,也就是说,接口是绕过抽象类的多重继承的拐杖。然后,允许抽象类的多重继承。默认方法的冲突没有成为问题。
Alex78191

你是对的。从Java 9开始,接口中允许使用私有方法。这些不能是抽象的,它们主要在其他默认或静态方法(如果它们本身是静态的)中在接口内实现和使用。
Stefanos Kargas

1
这个答案只是忽略了所提出的问题:为什么接口不能具有受保护的方法?受保护的方法仍然会“将方法暴露给外部世界”,并且声称“这些方法本质上是公共的”是错误的。它们是公共的,因为该语言是按这种方式设计的,但是它可以允许接口中使用受保护的方法。OP只是问为什么。
Skomisa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.