11

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

4

7 回答 7

12

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

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

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

本质上,内部类是范围的一种形式。包访问隐藏包外部的类。私有内部类将该类隐藏在该类之外。

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

void execute(Runnable r);

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

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

作为一个函数的指针。

于 2010-04-25T09:14:14.637 回答
5

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

普通或顶级类的实例可以独立存在。相比之下,如果不绑定到顶级类,则无法实例化内部类的实例。

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

我们有顶级汽车。Car 类的实例由 Wheel 类的四个实例组成。Wheel 的这种特定实现是特定于汽车的,因此代码没有对 Wheel 的一般概念进行建模,后者可以更好地表示为顶级类。因此,它在语义上与 Car 类相关联,Wheel 的代码以某种方式与其外部类耦合。

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

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

于 2010-04-25T09:19:46.500 回答
4

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 意义上的内部类,只有嵌套类。

另请参阅此内部类示例

于 2010-04-25T09:11:40.477 回答
3

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

于 2010-04-25T09:19:09.837 回答
2

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

ebookPrice 可以从 Price 继承,后者的范围更大,并且与其他所有类相关。

(只是我的两分钱)。

于 2010-04-25T09:17:59.103 回答
2

有些语言将内部类提升到完全不同的水平,例如 Beta 和新语。在这些语言中,类的嵌套用作包装(即没有包)。

有关此愿景的详细介绍,请参阅“我们需要多少模块概念?” 在对象团队博客上。另请参阅 Gilad Bracha 在他的博客上的工作......

于 2010-04-25T12:13:11.943 回答
1

面向对象的优势

依我的拙见,内部类最重要的特性是它允许您将事物转换为通常不会转换为对象的对象。与没有内部类的情况相比,这使您的代码更加面向对象。

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

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

组织优势

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

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 次比较的 switch 中,我们可以期望在平均情况下进行 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'

于 2013-09-04T13:01:58.947 回答