为什么我们使用内部类?

我想问你为什么我们需要内部类以及为什么要使用它们?
我知道如何使用内部类但我不知道为什么..

一些内部类是公开的(例如Java中的Map.Entry ),但这是例外而不是常态。

内部类基本上是一个实现细节。

例如,Swing广泛使用事件侦听器的内部类。 如果没有它们,你最终会用一堆你不需要看的类来污染全局命名空间(这可能会使它们的目的更难确定)。

基本上内部类是范围的一种forms。 包访问会从包外部隐藏类。 私有内部类将该类隐藏在该类之外。

Java中的内部类也可以替代缺少函数指针或方法委托(在C#中)或闭包。 它们是将函数传递给另一个函数的手段。 例如,在Executor类中,您有:

 void execute(Runnable r); 

所以你可以传入一个方法。在C / C ++中可以通过以下方式实现:

 void execute(void (*run)()); 

是一个函数的指针。

大多数时候我使用内部类是因为内部类与其他语言中可用的闭包概念最接近。 这样可以创建和使用内部嵌套作用域的对象,该对象可以访问其外部作用域的变量。 这通常在创建回调(例如,在Swing中定义各种Listener )等方面很有用。

Java中的匿名内部类是一种使用适配器模式的方法。

 interface Bar { public void bar(); } class Foo { public void foo() { // do something relevant } // it happens that foo() defines the same contract (or a compatible one) as // Bar.bar(); with an anonymous inner class we can adapt Foo to the Bar // interface public Bar asBar() { // return an instance of an anonymous inner class that implements // the Bar inteface return new Bar() { public void bar() { // from an inner class, we can access the enclosing class methods // as the "this pointers" are "linked" foo(); } }; } } 

在Java中,确保您了解内部类和嵌套类之间的区别 :

内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段

C#没有Java的内部类,只有嵌套类。

另请参见此内部类示例 。

来自维基百科的这篇文章可能会帮助您理解为什么我们需要一个内部类:

普通或顶级类的实例可以单独存在。 相反,如果没有绑定到顶级类,则无法实例化内部类的实例。

让我们采用四轮汽车的抽象概念。 我们的车轮具有特定的function,依赖于我们的汽车的一部分。 这个概念并不代表车轮作为可能是车辆一部分的更一般forms的车轮。 相反,它代表它们特定于此。 我们可以使用内部类对这个概念进行建模,如下所示:

我们有顶级的汽车。 Class Car的实例由Wheel类的四个实例组成。 Wheel的这种特殊实现特定于汽车,因此代码不会模拟轮子的一般概念,这将更好地表示为顶级等级。 因此,它在语义上连接到类Car,而Wheel的代码在某种程度上耦合到它的外部类。

内部类为我们提供了准确建模此连接的机制。 我们说我们的轮子类是Car.Wheel,Car是顶级类,Wheel是内部类。

因此,内部类允许程序的某些部分的面向对象,否则这些部分将被封装到类中。

我使用它们作为范围,例如,如果我有类电子书并且我有ebookPrice,我将ebookPrice包含在电子书类之间,因为它与它相关并且只能在其中使用(至少在概念上)。

ebookPrice可以从Price更高的范围inheritance,并且与其他所有类相关。

(只是我的两分钱)。

有些语言将内部类带到了不同的级别,比如Beta和Newspeak。 在这些语言中,类的嵌套用作包装(即没有包)。

为了更好地了解这一愿景,请参阅“我们需要多少个模块概念?” 在对象团队博客上。 另见Gilad Bracha在他博客上的作品……

面向对象的优势

在我看来,内部类最重要的特性是它允许你把东西变成你通常不会变成对象的东西。 这使得您的代码比没有内部类更加面向对象。

我们来看看会员class。 由于其实例是其父实例的成员,因此它可以访问父实例中的每个成员和方法。 乍一看,这似乎并不多; 我们已经从父类的方法中获得了那种访问权限。 但是,成员类允许我们从父进程中取出逻辑并将其客观化。 例如,树类可以有一个方法和许多辅助方法来执行树的搜索或遍历。 从面向对象的角度来看,树是树,而不是搜索算法。 但是,您需要熟悉树的数据结构才能完成搜索。

内部类允许我们删除该逻辑并将其放入自己的类中。 因此,从面向对象的角度来看,我们已经将function从它不属于的地方中取出并将其放入自己的类中。 通过使用内部类,我们成功地将搜索算法与树分离。 现在,要更改搜索算法,我们可以简单地交换一个新类。 我可以继续,但这开启了我们的代码,以实现面向对象技术提供的许多优势。

组织优势

面向对象的设计并不是每个人的事情,但幸运的是,内部类提供了更多。 从组织的角度来看,内部类允许我们通过使用命名空间来进一步组织我们的包结构。 不是将所有内容都转储到平面包中,而是可以在类中进一步嵌套类。 显然,没有内部类,我们仅限于以下层次结构:

 package1 class 1 class 2 ... class n ... package n 

对于内部类,我们可以执行以下操作:

 package 1 class 1 class 2 class 1 class 2 ... class n 

仔细使用,内部类可以提供更自然地适合您的类的结构层次结构。

回调优势

内部成员类和匿名类都提供了一种定义回调的便捷方法。 最明显的例子涉及GUI代码。 但是,回调的应用可以扩展到许多域。

大多数Java GUI都有某种组件来启动actionPerformed()方法调用。 不幸的是,大多数开发人员只是让他们的主窗口实现ActionListener。 因此,所有组件共享相同的actionPerformed()方法。 要确定哪个组件执行了操作,actionPerformed()方法中通常会有一个巨大的,丑陋的开关。

这是一个单片实现的例子:

 public class SomeGUI extends JFrame implements ActionListener { protected JButton button1; protected JButton button2; //... protected JButton buttonN; public void actionPerformed(ActionEvent e) { if (e.getSource() == button1) { // do something } else if (e.getSource() == button2) { //... you get the picture } } } 

每当你看到开关或大的if / if else块时,响亮的警钟应该开始响起你的脑海。 通常,这样的构造是不好的面向对象设计,因为代码的一个部分的改变可能需要在switch语句中进行相应的改变。 内部成员类和匿名类允许我们远离切换的actionPerformed()方法。

相反,我们可以定义一个内部类,为我们想要监听的每个组件实现ActionListener。 这可能会导致许多内部类。 但是,我们可以避免使用大型switch语句,并且可以更好地封装我们的操作逻辑。 而且,这种方法可以提高性能。 在有n次比较的交换机中,我们可以预期在平均情况下进行n / 2次比较。 内部类允许我们在动作执行者和动作监听器之间建立1:1的对应关系。 在大型GUI中,此类优化可对性能产生重大影响。 匿名方法可能如下所示:

 public class SomeGUI extends JFrame { // ... button member declarations ... protected void buildGUI() { button1 = new JButton(); button2 = new JButton(); //... button1.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { // do something } }); // .. repeat for each button } } 

使用内部成员类,相同的程序将如下所示:

 public class SomeGUI extends JFrame { ... button member declarations ... protected void buildGUI() { button1 = new JButton(); button2 = new JButton(); ... button1.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { // do something } } ); .. repeat for each button 

由于内部类可以访问父类中的所有内容,因此我们可以将单个actionPerformed()实现中出现的任何逻辑移动到内部类。

我更喜欢使用成员类作为回调。 但是,这是个人偏好的问题。 我只是觉得太多的匿名类杂乱无比的代码。 我也觉得如果匿名类大于一两行,它们就会变得笨重。

缺点是什么?

和其他任何东西一样,你必须把好处带走。 内部阶级有其缺点。 从维护的角度来看,没有经验的Java开发人员可能会发现内部类很难理解。 内部类的使用也会增加代码中的类总数。 此外,从开发的角度来看,大多数Java工具在内部类的支持方面都略显不足。 例如,我使用IBM的VisualAge for Java进行日常编码。 内部类将在VisualAge中编译,但没有内部类浏览器或模板。 相反,您只需将内部类直接键入类定义即可。 不幸的是,这使得浏览内部阶级变得困难。 由于在键入类定义或使用内部类时丢失了许多VisualAge的代码完成辅助工具,因此也很难键入