我想问你为什么我们需要内部类,为什么要使用它们?
我知道如何使用内部类,但我不知道为什么..为什么我们使用内部类?
回答
一些内部类是公开暴露的(例如,在Java中为Map.Entry
),但这是迄今为止的例外情况而非标准。
内部类基本上是一个实现细节。
例如,Swing为事件监听器广泛使用内部类。没有它们,你最终会用一堆你不需要看的类(这可能使他们的目的难以确定)污染全局命名空间。
本质上,内部类是范围的一种形式。包访问隐藏了包之外的类。私人内部班级从该班级以外隐藏该班级。
Java中的内部类也是缺少函数指针或方法委托(在C#中)或闭包的替代品。它们是将函数传递给另一个函数的手段。例如,在Executor
类您有:
void execute(Runnable r);
所以可以传递的方法中的在C/C++可与实现:
void execute(void (*run)());
是一个指针的函数。
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中,请确保您了解difference between inner classs and nested class:
一个内部类与它的外围类的 实例相关联,并 拥有该对象的 方法和字段
直接访问
从Java的意义上说,C#没有内部类,只有嵌套类。
另请参阅Inner Class Example。
例如,如果我有电子书类,并且有ebookPrice,我会将ebookPrice放在电子书类之间,因为它与它相关并且只能在其内部使用(至少在概念上)。
ebookPrice可能继承价格在更高的范围内,并且与其他每个类相关。
(只是我的两美分)。
大部分时间我使用内部类是因为内部类是最接近closure概念可用其他语言。这使得可以创建和使用可以访问其外部变量的内部嵌套作用域的对象。这通常用于创建回调(例如,在Swing中定义各种Listener
)等。
这件作品从wikipedia可能会帮助你理解为什么我们需要一个内部类:
正常或顶级类的一个实例可以在其自己存在。相比之下,一个内部类的实例不能被绑定到顶级类实例化。
让我们拿四个车轮的汽车的抽象概念。我们的车轮有一个特定的功能,依靠我们的车的一部分。这个概念并不代表车轮可以作为车辆的一部分,而是更为一般的车轮。相反,它表示它们是特定于此的。我们可以使用内部类对这个概念进行建模,如下所示:
我们有顶级汽车。 Class Car的实例由Class Wheel的四个实例组成。 Wheel的这个特定实现是专用于汽车的,因此代码不能模拟Wheel的一般概念,它可以更好地表示为顶级类。因此,它在语义上连接到Car类,并且Wheel的代码以某种方式耦合到它的外部类。
内部类为我们提供了一种机制来准确建模此连接。我们说我们的轮班是Car.Wheel,Car是顶级班,Wheel是班级。
因此,内部类允许程序某些部分的对象方向,否则这些部分不会被封装到一个类中。
有些语言会把内部类放到一些不同的级别,比如Beta和Newspeak。在这些语言中,类的嵌套作为包装(即没有包)。
为了很好地理解这一愿景,请参阅对象团队博客上的"How many concepts for modules do we need?"。另见吉拉德·布拉彻工作的his blog ...
面向对象的优势
依我拙见,内部类中最重要的特点是,它可以让你把东西放进对象你通常不会变成对象。这可以让你的代码更加面向对象,而不用没有内部类。
让我们来看看成员类。由于其实例是其父实例的成员,因此它有权访问父代中的每个成员和方法。乍一看,这可能看起来不多;我们已经从父类的方法中获得了这种访问。但是,该成员类允许我们从父项中取出逻辑并将其对象化。例如,树类可能有一个方法和许多辅助方法来执行树的搜索或行走。从面向对象的角度来看,树是一棵树,而不是搜索算法。但是,您需要深入了解树的数据结构才能完成搜索。
内部类允许我们删除该逻辑并将其放入其自己的类中。所以从面向对象的角度来看,我们已经将功能从不属于它的地方带入了自己的类。通过使用内部类,我们已经成功地将搜索算法从树中解耦。现在,为了改变搜索算法,我们可以简单地交换一个新的类。我可以继续,但是这打开了我们的代码,以获得面向对象技术提供的许多优点。
的组织优势
面向对象的设计是不是每个人的事情,但幸运的是,内部类提供更多。从组织角度来看,内部类允许我们通过使用名称空间来进一步组织我们的包结构。类可以进一步嵌套在类中,而不是将所有东西都放在一个扁平包中。明确地说,没有内部类,我们仅限于以下层次结构:
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
}
}
}
每当你看到交换机或大型如果/当else块,响亮的警钟应该开始在你的心中响起。通常,这样的结构是不好的面向对象的设计,因为代码的一个部分的改变可能需要switch语句的相应改变。内部成员类和匿名类允许我们远离切换的actionPerformed()方法。
相反,我们可以定义一个内部类,为每个我们想要听的组件实现ActionListener。这可能会导致许多内部类。但是,我们可以避免大型交换语句,并且可以封装我们的动作逻辑。而且,这种方法可以提高性能。在有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的代码完成辅助工具,因此也很难打字。
- 1. 为什么我们在C#中使用内部类常量?
- 2. 为什么我们使用WebMvcAutoConfigurationAdapter类
- 3. 为什么我们在Mongo类的顶部使用@Inject注解?
- 4. 为什么我们可以从外部类访问内部类中的变量?
- 5. 为什么我们调用直接添加侦听器作为内部类?
- 6. 装配需要什么?为什么我们使用它们?
- 7. 为什么我们使用“类自我”表达?
- 8. 为什么使用方法局部抽象内部类
- 9. Redis占用了太多内存,为什么我们使用它?
- 10. 为什么以及何时使用静态内部类或实例内部类?
- 11. 为什么我们使用Groovy中
- 12. 为什么我们使用canvas.save或canvas.restore?
- 13. 为什么我们使用ChangeAwareList和ChangeAwareMap
- 14. 为什么我们使用AutoCompleteTextView
- 15. 为什么我们在jQuery中使用“({})”?
- 16. 为什么我们使用Git Keys
- 17. 为什么我们使用SimpleLoadTimeWeaver?
- 18. 为什么我们使用tf.name_scope()
- 19. 为什么我们使用tq_struct?
- 20. 为什么我们使用SWIFT
- 21. 为什么我们使用SpreadsheetApp.flush();?
- 22. 为什么我们使用jquery.klass?
- 23. 为什么我们使用.htaccess文件?
- 24. 为什么我们使用常量?
- 25. 为什么我们使用ViewTreeObserver#addOnGlobalLayoutListener()
- 26. 为什么我们使用var _show = false;
- 27. 为什么我们特别使用JMS?
- 28. 为什么我们不能在内部函数中访问'this'?
- 29. 为什么我们使用堆来存储内存?
- 30. CSP内容安全策略 - 为什么我们不使用它?