【问题标题】:What are the purposes of inner classes内部类的目的是什么
【发布时间】:2012-07-09 00:47:10
【问题描述】:

我正在回顾 Java 中内部类的概念。到目前为止,我所理解和应用的 java 内部类有一个链接或访问其外部/封闭类的方法和字段。

我的问题:

  1. 什么时候应该创建或定义一个内部类?
  2. 内部类是否被称为“帮助类”?
  3. 您创建内部类的指标是什么?它们的其他目的是什么?

【问题讨论】:

标签: java oop class inner-classes


【解决方案1】:

从我的个人笔记中添加,供未来的访问者使用:


资料来源:https://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html

【讨论】:

    【解决方案2】:

    从概念上讲,内部类可用于表示 Universe 中没有该父类型就不会存在的类型。换句话说,对于允许内部类的语言,类型都是“类型定义器”。然后可以将类型视为显式或隐式定义新类型的东西。

    例如,假设我们有一个“食物”可以应用于任何事物的宇宙。甚至自己。食物是我们宇宙中的一个基本概念。我们引入了一个名为 Meat 的 Food 子类。没有这个概念,就没有“肉食者”这样的东西。所以我们可以(注意“can”)定义一个嵌套类型“Meat.Eater”(它可以实现一个 IEater 接口)并将动物定义为不同 IEater 列表的包含结构。

    一旦我们将肉从宇宙中移除,肉食者就会消失。

    同样的理念巧妙地适用于更抽象和技术上有用的安排,例如 Memento 设计模式中的 Mementos、定义为嵌套类的配置对象,以及其他特定于类型的行为或结构。

    【讨论】:

      【解决方案3】:

      假设您在一个名为“com.custom.classes”的包中拥有一个类型和一个名为 OuterClass 的类。

      下面是你开始需要内部类或静态类的方式:

      案例 1:

      1. 你需要打包一组类
      2. 但还需要在该包级别向所有这些类公开某些全局变量
      3. 您知道您不能对包做这样的事情,但意识到您可以通过继承来实现这一点,其中父类成员可以充当全局变量,可供其所有子类实例使用。
      4. 但是您不喜欢需要继承父类并且需要实例化子类以访问全局变量的想法。这就像为了喝咖啡而要求买一家咖啡店。
      5. 因此您意识到您可以使用静态成员创建一个 OuterClass,并根据需要将这个 OuterClass 中的所有类作为内部类或静态类容纳,瞧! OuterClass 静态成员可用作这些嵌套类的全局变量,您甚至可以在不实例化它们的情况下访问它们。

      这段代码应该解释得更好

      public class InnerClassTester{
        public static void main(String []args){
      
          // without the need to instantiate any class
          // static class without instantiation
          System.out.println(OuterClass.NestedStaticClass1.incrGlobalNum()); // outputs 1
      
          // static class instantiated
          OuterClass.NestedStaticClass2 koolObj = new OuterClass.NestedStaticClass2();
          // works with instantiation as well
          System.out.println(koolObj.incrGlobalNum()); // outputs 2
      
          // inner classes always need to be instantiated
          // and they can only be instantiated from within an instance of outer class
          // think of them as instance member of outer class and this would make sense
          OuterClass.NestedInnerClass1 koolObj2 = new OuterClass().new NestedInnerClass1();
          // works with inner classes as well
          System.out.println(koolObj2.incrGlobalNum()); // outputs 3
       }
      }
      
      class OuterClass{
          // global variable thats only accessible for select classes (or nested classes)
          // we also learn a purpose for private static fields
          private static int privateGlobalValue = 0;
      
          // classes to be grouped
          // static class
          public static class NestedStaticClass1{
              // no need to instantiate this class to access/update the global value
              public static int incrGlobalNum(){
                  return ++privateGlobalValue;
              }
          }
      
          public static class NestedStaticClass2{
              // instantiate and still manipulate the global value
              public int incrGlobalNum(){
                  return ++privateGlobalValue;
              }
          }
      
          // inner class
          public class NestedInnerClass1{
              // instantiate and still manipulate the global value
              public int incrGlobalNum(){
                  return ++privateGlobalValue;
              }
          }
      
      }
      

      这是否让您想起 Javascript 中的闭包? :)


      嵌套类的大多数应用程序都认为它是基于设计决策而应用的。这意味着,嵌套类的每个案例都可以替换为其他设计。

      但话虽如此,我们也可以用组合模式代替继承模式(最近势头越来越大),尽管当类之间的依赖关系如此之大以至于组合模式时,继承模式肯定更好依赖关系完全是丑陋的。

      案例 2:

      1. 您需要在 OuterClass 上实现 2 个接口,IShark 和 IMosquito,它们具有相同的签名,一个公共的咬合方法。
      2. 但您想显示 2 条不同的消息,因为鲨鱼的叮咬与蚊子的叮咬略有不同。
      3. 但是您知道这是不可能的,因为只能实现一种咬合方法
      4. 您知道您可以在同一个包中创建 2 个不同的类,它们实现任一接口并实现单独的咬合方法并将它们组合在 OuterClass 中。
      5. 但是您想在 OuterClass 中完成它因为您的设计决定将咬行为封装在其中,可能是因为在类中存在对私有变量的依赖关系。
      6. 很快您就会意识到您可以通过私有静态内部类实现这两个接口,并使其在外界看来就像是组合而成的。

      看看这段代码:

      // no additional classes in the package
      
      public class InterfaceTester{
      
           public static void main(String []args){
      
              // same class returns 2 instances - both compliant to 
              // either interfaces and yet different output
      
              IShark shark = OuterClass.getSharkInstance();
              System.out.println(shark.bite()); // outputs "Die fast bosedk!"
      
              IMosquito mosquito = OuterClass.getMosquitoInstance();
              System.out.println(mosquito.bite()); // outputs "Die slow bosedk!"
           }
      
      }
      
      interface IShark{
          public String bite();
      }
      
      interface IMosquito{
          public String bite();
      }
      
      class OuterClass implements IShark{
      
          // dependency of inner class on private variable
          private static String dieSlow = "Die slow bosedk!";
          private static String dieFast = "Die fast bosedk!";
      
          private static OuterClass outerInst;
          private static InnerClass innerInst;
      
          // private constructor to stop regular instantiation
          private OuterClass(){}
      
          // get a shark !
          public static IShark getSharkInstance(){
              return outerInst != null ? outerInst : new OuterClass();
          }
      
          // get a mosquito !
          public static IMosquito getMosquitoInstance(){
              return innerInst != null ? innerInst : new InnerClass();
          }
          // an implementation of bite
          public String bite(){
              return dieFast;
          }
      
          // inner class that implements the second interface
          private static class InnerClass implements IMosquito{
              // different implementation of bite
              public String bite(){
                  return dieSlow;
              }
          }
      
      }
      

      这类设计决策案例很多,上面的所有答案都列出了几个这样的案例。因此,认为此功能更多地是作为一种新模式而不是作为一种特性或功能引入的,这并没有错。

      【讨论】:

        【解决方案4】:

        用于分组逻辑的内部类,例如,如果你有B类,而这个类只用在A类,那么最好把B类作为A类的内部类,这样可以提高可读性和代码的可重用性。

        快乐的代码:)

        【讨论】:

          【解决方案5】:

          内部类最适合对在一个地方使用的类进行逻辑分组。例如,如果您想创建仅由封闭类使用的类,那么为此创建单独的文件是没有意义的。相反,您可以将其添加为“内部类”

          根据java tutorial

          使用嵌套类的令人信服的原因包括:

          • 这是一种逻辑分组类的方法,仅在一个中使用 地点。
          • 它增加了封装性。
          • 它可以带来更易读和可维护的代码。

          【讨论】:

          • 这不是 Javadoc,它是 Java 教程,它是关于嵌套类,而不是内部类,这是一种特殊情况。
          • @user207421 是的教程部分我同意。那么,内部类不是嵌套类吗?但是本教程的第一行说“Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类”并且如果向下滚动一点标题为“内部类”,您会看到的部分恰好显示了类似的示例。
          【解决方案6】:

          内部类的一个目的是附加监听器。例如,假设您有一个JMenuItem。你可以让它退出你的应用程序,如下代码所示:

          JMenuItem quitItem = new JMenuItem("Quit");
          quitItem.addActionListener(new ActionListener(){
              public void actionPerformed(ActionEvent e)
              {
                  //cleanup code before exiting
                  System.exit(0);
              }
          });
          

          您可能还希望一个类能够访问完全从属于该类的外部类状态变量。例如,考虑编写一个简单的颜色计算器。它可能有一个文本区域,您可以在其中键入十六进制代码。当您按下回车键时,您希望 JPanel 显示颜色。这是您可能会做什么的粗略大纲。

          public class ColorCalc extends JPanel implements Runnable
          {
              Color displayedColor;
              JTextArea colorEnterArea;
              public ColorCalc()
              {
                  displayedColor = Color.white
                  colorEnterArea = new JTextArea();
              }
              public void run()
              {
                  //build GUI here
              }
              public static void main(String[] args)
              {
                   ColorCalc cc = new ColorCalc();
                   javax.swing.SwingUtilities.invokeLater(cc);
              }
              //subservient inner class with access to outer class state variable.
              class ColorPanel extends JPanel
              {
                   public void paintComponent(Graphics g)
                   {
                       g.setColor(displayedColor);
                       g.fillRect(0,0,getWidth(), getHeight());
                   } 
              }
          
          }
          

          【讨论】:

            【解决方案7】:

            作为一般规则,对象应设计单一职责(高度内聚)。换句话说,任何设计良好的对象都应该执行单一连贯的任务。这将被视为面向对象设计的最佳实践。

            然而,有时,开发人员可能会设计一个需要单独 专门类才能工作的类。这个单独的专门的类可以被认为是一个帮助类

            如果 helper没有被任何其他类使用,那么它将被视为 prime Candidate 作为 inner类

            正如上面 ncmathsadist 所引出的,内部类使用的示例将在 事件处理程序的实现中。

            例如,在设计图形用户界面 (GUI) 时,开发人员可能创建了一个 按钮,该按钮执行 特定 任务在用户按下它之后。

            该按钮需要一个事件处理程序,它侦听该特定按钮何时被按下。

            在这种情况下,将按钮的事件处理程序创建为 内部类 将是最佳实践,因为除了与GUI 类中的特定按钮。

            【讨论】:

              【解决方案8】:

              我只是认为这只是语言的一个特征。如果我们采用OOD并遵守SOLID原则,我不建议使用它。

              【讨论】:

                【解决方案9】:

                如果您发现有足够的代码可以由类更好地完成,因为类为我们提供了指定统计信息和 字段和方法的行为,并且您不希望此类需要在封闭类之外使用。你应该使用内部类。

                这里的内部类对外界是隐藏的。 内部类可以访问为我们提供封装的封闭类的私有成员。

                让我举个例子.. 假设您要将齿轮设置为循环,并且您有一个业务规则,例如最多只有 6 个齿轮。 因此,您可以创建内部类循环,它具有设置齿轮的方法。 该方法在设置齿轮之前进行了一些验证。就像循环正在运行...齿轮数小于 6...

                最好的例子是事件处理代码使用内部类(有时是匿名内部类)来创建事件和侦听器,而无需为您的事件创建单独的事件对象和事件侦听器类。..

                【讨论】:

                  【解决方案10】:

                  内部类的典型用途是在容器内实现迭代器(例如ArrayList - 查找class Itr)。所有容器想要暴露给世界其他地方的是Iterator。但是,它必须创建该迭代器的一些具体实现,可能熟悉容器的内部结构。使用内部类隐藏了实现,同时保持它靠近容器的实现。并且作为内部(即非静态),它绑定到该容器的特定实例,这使其可以访问私有容器成员。

                  a few types of inner classes - 非静态嵌套类、本地类和匿名类。每个都有不同的目的,所以在询问内部类时,你应该说明你在说什么类型。

                  假设您指的是非静态内部类,我会说使用它们的原因与使用常规类相同(即抽象并将代码划分为逻辑单元),但没有理由使用它对世界其他地方可见的类。当然,您也可以将嵌套类公开,在这种情况下,您可以让它们嵌套而不是独立,以表达它们与外部类的紧密关系。

                  【讨论】:

                  • 非静态嵌套类也称为内部类。它不是一种内部类。
                  【解决方案11】:
                  1. 请参阅Java tutorial 了解主要原因。

                  2. 如果您所说的“帮助类”是指仅供内部使用的东西,那么不,不一定。你可能想做类似的事情

                    class Outer {
                        private static class Inner implements InterestingInterface {
                            // whatever
                        }
                        public InterestingInterface make_something_interesting() {
                            return new Inner();
                        }
                    }
                    

                    这里,Inner 不是一个“帮助类”,因为外部世界确实可以看到它的实例,但它的实现是完全隐藏的——外部世界只知道它得到了一些实现 @ 的对象987654324@.

                  【讨论】:

                  • 所以这就是他们说内部类增加封装的原因之一?
                  • @user962206:是的。没有客户端甚至不需要知道他们收到的实际上是一个内部类的实例,或者这样的类存在于Outer中。
                  【解决方案12】:

                  这是一个风格问题。可以用内部类完成的任何事情也可以作为一系列外部类来完成。内部类对于轻量级或紧密绑定到封闭类的类特别有用。例如,比较器通常是这两种东西。它需要对类的实现有深入的了解,并且可能只有几行。它可能是内部类的理想候选者。

                  【讨论】:

                    猜你喜欢
                    • 2010-10-17
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-12-27
                    • 1970-01-01
                    • 2014-11-15
                    • 1970-01-01
                    • 2011-05-27
                    相关资源
                    最近更新 更多