何时在Java中使用内部类作为辅助类

82
如果我有一个类和一个帮助类来执行一些功能,把它们做成内部类是否有意义?
    public class Foo {
       private FooHelper helper;

       // constructor & any other logic

       public void doSomeThing() {
         helper.do();
       }
    }

    public class FooHelper {
        public void do() {
         // code
        }
    }
在上述情况下,将FooHelper作为内部类是否有意义?如果这听起来很愚蠢,我对使用情况有点困惑。
在上述情况下,将FooHelper作为内部类是否合适?如果这听起来有些奇怪,那是因为我对它的使用场景有些困惑。

http://javabeginnerstutorial.com/core-java-tutorial/inner-class/ - Vaibhav Jain
1
通常用于ActionListeners,以及当您不想向其他人公开调用时。 - me_digvijay
@Vaibhav Jain,感谢提供链接。我已经了解了语法,但是我有些困惑何时使用它以及是否存在任何权衡。 - Hild
@SayemAhmed 我不确定那是唯一的选项,似乎可能有多个替代方案,作为新手我不确定正确的方法,所以还在探索中。 - Hild
2
@AnilSharma:想一想这个问题:“为什么要将与thing无关的内容暴露给外部世界呢?”我的意思是,如果你需要一个功能(类及其方法),只有一个类需要它,而其他外部类不需要它或其功能,最好不要将此功能暴露给其他类。这样可以避免污染外部世界,从而实现更好的架构。 - me_digvijay
显示剩余2条评论
10个回答

95

是的,将其设置为内部类是很有意义的。如果没有其他类需要使用它,请将其设置为私有(private)。如果不需要独占访问外部类成员,则将其设置为静态嵌套类(static nested class),因为这样会占用更少的内存空间。

可以查看官方教程推荐 -

如果需要访问封闭实例(non-public fields and methods), 使用非静态嵌套类(内部类)。如果不需要访问则使用静态嵌套类(static nested class)。


1
什么时候应该使用静态内部类?从功能角度来看,是否有任何区别? - Hild
4
一个 static nested class(静态嵌套类)不能访问外部类的成员(除非它是静态的)。如果这符合你的要求,请将其声明为静态。相反,非静态内部类可以完全访问外部类的成员。 - MD Sayem Ahmed
@Hild 静态嵌套类不包含对其封闭类的合成引用。因此,除非您有特定原因使其非静态,否则始终应努力将其作为“静态嵌套类”进行定义。 “静态内部类”是一个技术上错误的术语。 - Geek
谢谢,这真的很有用。但是@eddieferetro说得对,为什么不使用私有方法呢? - Hild
静态嵌套类为什么需要更少的内存空间? - getsadzeg
显示剩余2条评论

25

来自JAVA SE文档

为什么要使用内部类?

它是一种逻辑上分组仅在一个地方使用的类的方法:如果一个类只对另一个类有用,则将其嵌入该类并将这两个类放在一起是合理的。嵌套这样的“辅助类”可使它们的包更加简洁。

它增加了封装性:考虑两个顶级类A和B,其中B需要访问A的成员,否则这些成员将被声明为私有部件。通过将类B隐藏在类A中,可以将A的成员声明为私有,并且B可以访问它们。此外,B本身也可以隐藏在外部世界之外。

它可以导致更易读且更易维护的代码:将小类嵌套在顶级类中可以将代码放置在更接近其使用位置的位置。

所以,将FooHelper作为内部类使用是有意义的。


22

如果您认为FooHelper将对Foo以外的其他类完全无用,则将其作为Fooprivate 内部类是有意义的。这种设计的一个例子可以在HashMap中找到,它定义了一个私有内部类KeySet

否则,将其作为private实例看起来不错。


9
以下是内部类的一些用途:
  • 内部类用于获取比方法更好的对象功能。
  • 它们可用于需要执行多个操作且在类内具有良好重用性但不会被外部类的方法访问的情况。
  • 内部类是为了实现多重继承而设计的。
  • 当内部类在类上下文中有用时,就会使用它们。
  • 它们用于在类内部分离逻辑。
所以,如果您有符合上述点的需求,可以使用内部类。将内部类设置为private始终是更好的做法,以防止其他类访问。在您的情况下,使用内部类有助于使代码可读并将逻辑与外部类分离。

3

根据Oracle文档,简单解释:

使用嵌套类的强有力理由包括以下几点:

逻辑上将只在一个地方使用的类进行分组:如果一个类仅对另一个类有用,则将其嵌入该类中并使两者保持在一起是合理的。这样嵌套“helper类”会使其包更加流畅。

增加封装性:考虑两个顶层类A和B,其中B需要访问通常被声明为私有的A成员。通过将B类隐藏在A类中,可以将A的成员声明为私有,并且B可以访问它们。此外,B本身也可以隐藏在外部世界。

可以导致更易读和可维护的代码:将小类嵌套在顶层类中,将代码放置在使用它的位置附近。


2
内部类在它们很小且不需要名称时是有意义的。GUI中的监听器是它们有意义的经典示例。
如果类很大且重要,则应命名并放置在单独的文件中。
通常,普通GUI示例中的监听器类只做一件微小的事情,通常只是转发到其他函数来进行真正的工作。
我还经常使用静态嵌套类(技术上不是内部类)用于仅在另一个类的上下文中使用的类 - Map.Entry 就是一个很好的例子。它仅与Map一起使用,因此将Entry的定义作为Map接口的一部分具有组织意义。

我通常不太需要其他类型的嵌套类,比如非静态成员类和局部类。但是它们有时候确实很有用。一个合法使用成员类的好例子是看看LinkedList.ListItr的源代码。这是一个私有内部类,其目的是为LinkedList提供ListIterator的实现。为了做到这一点,有必要访问LinkedList中的私有数据。如果仅使用顶级类来实现,将需要在LinkedList中公开更多的公共方法,以允许ListIterator访问LinkedList的底层实现。相反,使用内部类使得LinkedList可以保持其实现私有,正如应该的那样。


抱歉,我是一个永远的新手,想要解释一下,因为我看不到内部类在面向对象编程世界中的位置:它们可以访问外部类的私有成员...你不能重复使用它们,只能通过复制粘贴...使代码变得丑陋,更难以阅读和维护...(继续) - inigoD
我只理解你在优化应用程序时不暴露太多公共方法的论点,但是无法看出在GUI中有什么意义:监听器模式使您可以在执行时更改项目行为(例如,在按钮“onclick”事件中要执行什么操作)。如果监听器是内部类,则通常表示您实际上不需要监听器功能:您想要一个永远不会重用的“onClick”方法(如通常在Android中发生的情况)。 - inigoD
2
@hild 但是,如果它是针对特定类的特定对象,为什么不在外部类中创建私有方法而不创建新的内部类呢? - inigoD
@eddieferetro 内部类只是一种清晰地将某些实际属于原始外部类的功能分离出来的方式。如果您的外部类中的某个功能在一个单独的类中实现,那么它会更加清晰明了。即使它在一个单独的类中,该功能也与外部类的工作方式非常紧密相关。 - code_fish
@eddieferetro 请看这里 http://www.javaworld.com/javaqa/2000-03/02-qa-innerclass.html - code_fish
显示剩余2条评论

1
使用内部类的优点是可以访问外部类的成员。在您的情况下,如果您认为FooHelper不会被任何其他类使用,可以将其设为内部类。
要检查内部类的实用性,请查看AWT的示例。匿名内部类在事件处理程序中被广泛使用。

0

Foo的作用域是什么?当Foo领域模型类,具有自己的生命周期,而helper是常见服务时,似乎混合了两个具有非常不同作用域/生命周期的对象。

通常,领域实体从创建、持久化到GC都有自己的生命周期。另一方面,帮助程序或服务要么是静态的,要么是更好的动态,其生命周期等于整个应用程序,例如Spring Bean。

一旦您的领域实体包含服务的引用,它可能会给您带来严重的问题。例如,每次调用存储库的Get都需要将该服务的引用注入到领域实体中。我建议避免使用此模式。

对我来说,不清楚谁会为您创建Helper的实例。


0

是的,Hild,在许多情况下使用内部类是有意义的。

可以这样想 - 内部类将与外部类一起存在和消失,因此任何特定于外部类所需的功能都可以添加到内部类中。 最常见的例子是 - 大多数情况下的监听器 - KeyListeners、MouseListeners、eventListeners等类型。

Java中的类允许您拥有特定的功能,但有时您可能需要具有单独的专业功能,但它也需要与您设计的类密切相关。

内部类可以分为四种类型。 简单的谷歌搜索可以帮助您了解更多信息。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接