Java 具有较高可见性的重写方法是否是一种良好的做法?

Java 具有较高可见性的重写方法是否是一种良好的做法?,java,overriding,class-design,Java,Overriding,Class Design,回答这个问题:我已经说过: 您没有正确覆盖paintComponent()。这是一个受保护的 方法,而不是公共的。如果在此方法上添加@覆盖注释 然后编译器会抱怨 但@peeskillet明智地指出: 编译器不会抱怨公共或受保护 paintComponent。可以使用更高的可见性替代,但不能使用 下一个public高于protected,因此没有问题 这当然是真的。但现在出现了这样一个问题:这是一个具有更高可见性的良好实践吗 补遗 链接到javadoc 一点也不抱怨的Netbeans图像: 如果需

回答这个问题:我已经说过:

您没有正确覆盖
paintComponent()
。这是一个受保护的 方法,而不是公共的。如果在此方法上添加
@覆盖
注释
然后编译器会抱怨

但@peeskillet明智地指出:

编译器不会抱怨
公共
受保护
paintComponent
。可以使用更高的可见性替代,但不能使用 下一个
public
高于
protected
,因此没有问题

这当然是真的。但现在出现了这样一个问题:这是一个具有更高可见性的良好实践吗

补遗 链接到javadoc

一点也不抱怨的Netbeans图像:


如果需要从方法所在的类/子类外部访问该方法,那么解决方案是使用public参数覆盖可见性。最佳做法是尽可能使变量和方法处于最低的可见性。

重写的方法与所有其他方法一样,只有在希望外部代码能够调用它们时才应声明为公共方法(除非您别无选择,因为重写的方法已声明为公共方法)。在您的情况下,被重写的方法
paintComponent
只能由Swing调用,因此保持其
受保护是最好的选择。

您可以提高方法的可见性,使其变得更可见,而不是更不可见,因此paintComponent可以被重写并声明为
public
方法。话虽如此,我还要补充一点,你不应该这样做。当重写该方法时,您应该保持可见性不变,除非您有很好的理由使其更可见。

来自Java教程:

如果其他程序员使用您的类,您希望确保 不可能发生滥用。访问级别可以帮助您做到这一点

  • 使用对特定成员有意义的最严格访问级别。除非你有很好的理由不这样做,否则请使用private
  • 避免使用除常量以外的公共字段。(本教程中的许多示例都使用公共字段。这可能有助于说明一些示例
    要点简洁,但不建议用于生产代码。)公共 字段倾向于将您链接到特定的实现,并限制您的 更改代码的灵活性
这项建议旨在:

要实现最佳封装(信息隐藏),您应该 始终声明可见性最低且有效的方法。小量 程序确实没有问题,但在大型程序中,这个问题 过度耦合的问题非常严重。当一个零件 取决于另一个具体实施。更耦合 因为太多,所以进行更改的成本就越高 代码取决于具体的实现。这导致了软件的腐朽 程序变得越来越不可用,因为它不容易使用 升级


因此,增加可见性实际上不是一个好主意。这样的代码可能会给未来的开发和维护带来麻烦。

从面向对象的角度来看,它没有问题。通过扩展类,可以执行以下操作:

  • 更改某些功能(通过重写方法)
  • 扩展类的接口(通过添加新的公共方法)
当您重写一个方法并更改其可见性时,您同时在做两件事:很明显,您在更改功能的同时也扩展了接口。从类“客户端”的角度来看,实际上您正在类“接口”中创建一个新方法。这个新方法恰好与超类中的某个内部方法同名,但客户机并不关心(或者甚至不知道)。那你为什么不呢

另一方面,还有一个问题“你为什么需要这样做?”。超类的作者可能对这个方法的可见性有一些想法,并发现它的功能并不适合外部世界。 我并不是说这样做是错误的,但是您必须质疑您提高可见性的动机,因为这可能暗示您或超类的代码中可能存在糟糕的设计


顺便说一句:正如所指出的,不允许这种语言功能甚至可能是有害的。

为了补充Francois所说的内容,这里的OOP原则是“,”表示您应该能够扩展,但不能修改对象正如Francois指出的那样,通过覆盖方法来提高方法的可见性只是一种扩展。

这样做的一个原因是,如果您有一种方法需要在项目的其他地方覆盖,而当前范围不允许您覆盖。通常在使用默认值而不是受保护时

通过在项目中其他地方的正确包中创建一个新的子类(修改了作用域),然后可以在需要的代码中创建一个匿名类,因为现在您可以覆盖这个麻烦的方法,而不必处理反射(这会导致代码非常不可读)

这也是库类永远不会是最终类的原因,因为这样您就不能做这样的事情


编辑:我被要求详细说明为什么这与依赖注入相关。我只能根据自己的经验说话,先是和Guice,现在是Dagger

Dagger使用构造函数注入,这基本上意味着一个类将获得它的所有依赖项,作为构造函数的参数提供(并且只有在那里),并且绑定这些依赖项的粘合代码列在Dagger@模块中。在这个模块中,返回一个库类的子类通常非常方便,该类使用log语句进行扩充,或者提供一个定制的toString()方法。为了在没有反射的情况下真正做到这一点