Java中提供了定义内部类的选择,这一机制使得代码的书写更为方便和优雅(功能上相关的代码被紧密的组织在了一起)。
需要注意的是,内部类和传统的组合(即将一个类的实体定义为另一个类的成员)是完全不同的,其主要特性表现在以下的一些方面:
1、外围类的实例并不一定具有内部类的实例,除非显示的将内部类的实例定义为外围类的数据成员。
public class Outer{
int no;
public class Inner{}
}
这里类inner为类outer的内部类,但是在类outer内部只定义了inner类,并没有将其作为outer一定要具有的成员。
Example:
Outer instanceA= new Outer();
此时生成的类Outer的实例instanceA,只具有数据成员no(int),并无内部类Inner的实例。由于类Outer中并未定义类型为Inner的数据成员,Outer中是没有维持Inner实例的。
!由于内部类的语法与一般的OOP语言语法不同,十分容易按照“组合”的常规思想来对其进行理解,容易造成错误。
!其实Java中的内部类实例和其外围类是完全的独立,内部类具有其外围类的引用(有编译器在其生成的时候自动捕获),所以内部类可以十分自由的操作外围类中的域和方法(非常重要)。
2、由于内部类和其外围类是完全独立的(即内部类的实例可以不是外围类的成员,内部类可以独立执行自己的操作),可以允许一个外围类与多个内部类实例相关(hook:挂钩)。
!注意此处的独立,指在操作上的独立(可以独立作为一个实例进行操作),并非存在上的独立。内部类实例必须要与外围类实例hook在一起存在,不然其无法操作外围类的成员,也就失去了内部类存在的价值。一个外部类实例允许多个内部类实例的同时hook。
Example:
public class Outer{
int no;
class Inner implents InterfaceA{}
}
interface InterfaceA{}
main()
{
Outer instanceA= new Outer();
InterfaceA interfaceA=instanceA.new Inner() ;//注意内部类实例产生的方法, 外围类实例.new 内部类名() ,这样做的目的是为了保证内部类实例一定有外围类实例hook.
InterfaceA interfaceB=instanceA.new Outer();//又生成了一个内部类的实例,且也挂靠在了实例instanceA上。
}
!注意到此处进行了一个转型:将Inner向上转型为InterfaceA,这在内部类的使用中是十分重要和常见的。(与设计模式紧密的相关)。内部类在定义时,经常需要继承某一类或者接口,从而可以保证根据接口进行编程的有效性,有效的对程序进行了解耦。
3、内部类提供了一种对同一接口或同一基类,多样化的实现方式。
Example: 在设计模式中十分有名的迭代器模式(Iterator)就是一个非常成功的应用。
public Array
{
/* 数据成员的定义省去 */
//提供一种按序遍历的迭代器
Iterator sortedInterator(){
return Iterator{
boolean hasNext(){};
String next(){};
void remove(){};
};
}
//提供一种逆向遍历的迭代器
Iterator reverseInterator(){
return Iterator{
boolean hasNext(){};
String next(){};
void remove(){};
};
}
}
interface Iterator{
public boolean hasNext();
public String next();
public void remove();
}
以上的代码针对Interator提供了两种完全不同的实现,这在不使用内部类的情况下是十分困难的。
当需要使用相应的迭代器的时候,只需调用相应的迭代器生成方法即可返回该对象:sortedInterator()和reverseInterator()。
这里为了编写的方法,使用了两个实现了Interator的匿名类,它们都将被转型为Interator类,然后调用相应的接口功能。这在不使用内部类的情况下,是十分难以达到的!