我正在回顾 Java 中内部类的概念。到目前为止,我所理解和应用的 java 内部类有一个链接或访问其外部/封闭类的方法和字段。
我的问题:
- 什么时候应该创建或定义一个内部类?
- 内部类是否被称为“帮助类”?
- 您创建内部类的指标是什么,它们的其他目的是什么?
我正在回顾 Java 中内部类的概念。到目前为止,我所理解和应用的 java 内部类有一个链接或访问其外部/封闭类的方法和字段。
我的问题:
内部类最适合对在一处使用的类进行逻辑分组。例如,如果您想创建仅由封闭类使用的类,那么为此创建单独的文件是没有意义的。相反,您可以将其添加为“内部类”
按照java教程:
使用嵌套类的令人信服的原因包括:
- 这是一种对仅在一个地方使用的类进行逻辑分组的方法。
- 它增加了封装。
- 它可以导致更可读和可维护的代码。
内部类的一个经典用途是在容器内实现迭代器(例如ArrayList - 查找class Itr
)。所有容器想要暴露给世界其他地方的是一个Iterator
. 但是,它必须创建该迭代器的一些具体实现,可能熟悉容器的内部结构。使用内部类隐藏了实现,同时保持它靠近容器的实现。并且作为内部(即非静态),它绑定到该容器的特定实例,这允许它访问私有容器成员。
有几种类型的内部类——非静态嵌套类、本地类和匿名类。每个都有不同的目的,所以当询问内部类时,你应该指定你在说什么类型。
假设您指的是非静态内部类,我会说使用它们的原因与使用常规类(即抽象并将代码划分为逻辑单元)相同,但是没有理由使这种对类的使用可见到世界其他地方。当然,您也可以将嵌套类公开,在这种情况下,您可以使它们嵌套而不是独立,以表达它们与外部类的紧密关系。
有关主要原因,请参阅Java 教程。
如果您所说的“帮助类”是指仅供内部使用的东西,那么不,不一定。你可能想做类似的事情
class Outer {
private static class Inner implements InterestingInterface {
// whatever
}
public InterestingInterface make_something_interesting() {
return new Inner();
}
}
在这里,Inner
不是外部世界确实可以看到它的实例的“帮助类”,但它的实现是完全隐藏的——外部世界只知道它得到了一些实现的对象InterestingInterface
。
作为一般规则,对象应该为单一职责(高度内聚)而设计。换句话说,任何设计良好的对象,都应该执行一个连贯的任务。这将被认为是面向对象设计的最佳实践。
然而,有时,开发人员可能会设计一个需要单独的 专用类才能工作的类。这个单独的专业类可以被认为是一个助手类。
如果辅助类没有被任何其他类使用,那么它将被视为内部类的主要候选者
正如上面的ncmathsadist所引出的,内部类使用的一个例子是在事件处理程序的实现中。
例如,在设计图形用户界面 (GUI) 时,开发人员可能已经创建了一个按钮,该按钮在用户按下后执行特定任务。
该按钮需要一个事件处理程序来监听该特定按钮何时被按下。
在这种情况下,将按钮的事件处理程序创建为内部类将是最佳实践,因为除了GUI 类中的特定按钮之外,不会在其他任何地方使用 内部类。
内部类的目的之一是附加侦听器。例如,假设您有一个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());
}
}
}
这是一个风格问题。可以用内部类完成的任何事情也可以作为一系列外部类来完成。内部类对于轻量级或紧密绑定到封闭类的类特别有用。例如,比较器通常是这两种东西。它需要对类的实现有深入的了解,并且可能只有几行。它可能是作为内部类的理想人选。
如果您发现有足够的代码可以由类更好地完成,因为类为我们提供了使用字段和方法指定统计信息和行为,并且您不希望此类需要在封闭类之外使用。你应该使用内部类。
在这里,内部阶级对外界是隐藏的。内部类可以访问为我们提供封装的封闭类的私有成员。
让我举个例子。假设您要将齿轮设置为循环,并且您有一个业务规则,例如最多只有 6 个齿轮。因此,您可以创建内部类循环,它具有设置齿轮的方法。该方法在设置齿轮之前进行了一些验证。就像循环正在运行...齿轮数小于 6...
最好的例子是事件处理代码使用内部类(有时是匿名内部类)来创建事件和侦听器,而无需为您的事件创建单独的事件对象和事件侦听器类。
用于对类进行逻辑分组的内部类,例如,如果你有 B 类,而这个类只在 A 类中使用,那么最好将 B 类作为 A 类的内部类,因为这样可以提供可读性和可重用性你的代码。
快乐的代码:)
从概念上讲,内部类可用于表示 Universe 中没有该父类型就不会存在的类型。换句话说,对于允许内部类的语言,类型都是“类型定义器”。然后可以将类型视为显式或隐式定义新类型的东西。
例如,假设我们有一个“食物”可以应用于任何事物的宇宙。甚至自己。食物是我们宇宙中的一个基本概念。我们引入了一个名为 Meat 的 Food 子类。没有这个概念,就没有“肉食者”这样的东西。所以我们可以(注意'can')定义一个嵌套类型“Meat.Eater”(它可以实现一个IEater接口)并将动物定义为不同IEater列表的包含结构。
一旦我们将肉从宇宙中移除,肉食者就会消失。
同样的理念巧妙地适用于更抽象和技术上有用的安排,例如 Memento 设计模式中的 Mementos、定义为嵌套类的配置对象以及其他特定于类型的行为或结构。
假设您在一个名为“com.custom.classes”的包中拥有一个类型和一个名为 OuterClass 的类。
那么这里是你如何开始需要一个内部类或静态类:
情况1:
这段代码应该解释得更好
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 中的闭包?:)
嵌套类的大多数应用程序都将其应用在设计决策的基础上。这意味着,嵌套类的每个案例都可以替换为其他设计。
但是话虽如此,我们也可以用组合模式代替继承模式(最近势头越来越大),尽管当类之间的依赖关系如此之多以至于完全组合依赖时,继承模式肯定更好会很丑。
案例二:
看看这段代码:
// 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;
}
}
}
这类设计决策案例很多,上面的所有答案都列出了几个这样的案例。因此,认为这个特性是作为一种新模式而不是作为特性或功能引入的,这并没有错。
我只是认为这只是语言的一个特征。如果我们采用OOD并遵守SOLID原则,我不建议使用它。