内部阶级的目的是什么?

时间:2012-07-09 15:22:32

标签: java oop class inner-classes

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

我的问题:

  1. 什么时候应该创建或定义内部类?
  2. 是被认为是“助手类”的内部类?
  3. 你有什么指标可以帮助他们建立一个内部阶级,以及他们的目的是什么?

10 个答案:

答案 0 :(得分:38)

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

根据java tutorial

  

使用嵌套类的令人信服的理由包括:

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

答案 1 :(得分:13)

内部类的经典用法是在容器内部实现迭代器(例如ArrayList - 查找class Itr)。所有容器想要暴露给世界其他地方的是Iterator。但是,它必须创建该迭代器的一些具体实现,可能熟悉容器的内部。使用内部类隐藏了实现,同时保持它接近容器的实现。在内部(即非静态)中,它被绑定到该容器的特定实例,从而允许它访问私有容器成员。

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

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

答案 2 :(得分:4)

  1. 主要原因请参阅Java tutorial

  2. 如果通过“助手类”表示仅供内部使用的东西,那么不,不一定。您可能想要执行类似

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

    在这里,Inner不是一个“辅助类”,外界确实可以看到它的实例,但它的实现是完全隐藏的 - 外界只知道它得到一些对象实现InterestingInterface

答案 3 :(得分:3)

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

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

您可能还希望一个类可以访问外部类状态变量,这些变量完全服从于该类。例如,考虑编写一个简单的颜色计算器。它可能有一个文本区域,您可以在其中键入十六进制代码。当您按Enter键时,您希望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());
         } 
    }

}

答案 4 :(得分:3)

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

但是,有时开发人员可能会设计一个需要单独 专业类的课程才能工作。这个单独的专业类可以被视为帮助类

如果帮助未被任何其他类使用,那么它将被视为主要候选作为内部类

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

例如,在设计图形用户界面(GUI)时,开发人员可能已创建按钮执行 特定 任务

该按钮需要事件处理程序侦听以便按下该特定按钮。

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

答案 5 :(得分:2)

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

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

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

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

用于对类逻辑进行分组的内部类,例如,如果您有B类,并且该类仅在A类中使用,那么最好将B类作为内部类放在A类中,因为这样可以提高可读性和代码的可重用性。

快乐代码:)

答案 8 :(得分:0)

假设您在一个名为“ 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;
        }
    }

}

这是否使您想起Java语言中的闭包? :)


大多数嵌套类的应用程序都会看到它是根据设计决策来应用的。这意味着,嵌套类的每种情况都可以用其他设计替换。

但是话虽如此,我们也可以用构图模式代替继承模式(并且近来势头越来越大),尽管当类之间的依赖性如此之大以至于组成依赖关系将很难看。

案例2:

  1. 您需要在OuterClass上实现两个具有相同签名的接口,分别是IShark和IMosquito。
  2. 但是您想要显示2条不同的消息,因为鲨鱼的叮咬与蚊子的叮咬有点不同。
  3. 但是您知道这是不可能的,因为只能执行一种咬合方法
  4. 您知道您可以在同一包中创建两个不同的类,这些类可以实现两个接口,也可以实现单独的bite方法,并在OuterClass中进行组合。
  5. 但是您想在OuterClass中完成它,因为您的设计决定是将叮咬行为封装在其中,也许是因为该类中对私有变量的依赖。
  6. >
  7. 很快您就知道可以通过私有静态内部类来实现这两个接口,并使它们看起来像是组成了的。

看看这段代码:

// 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;
        }
    }

}

这类设计决策案例很多,以上所有答案都列出了几种此类案例。因此,可以认为该功能更多地是作为一种新模式引入的,而不是功能或功能的引入。

答案 9 :(得分:0)

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

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

一旦我们将 Meat 从宇宙中移除,Meat Eater 就会消失。

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