各种介绍语法的程序书上经常出现内部类InnerClass,尽管语法上并不复杂,但如果没有实际例子供参考的话,在概念上比较难以理解,也难以体会到内部类的精妙之处。
希望这篇博文能帮助你更好地理解内部类 ^_^
一个非常常见的例子:你要在JavaSwing里加一个按钮,点击按钮触发事件。
你知道要触发按钮的事件ActionEvent,就得实现ActionListener接口。通过addActionListener(this)来监听。在事件发生时调用ActionListener接口里的actionPerformed方法。
示意代码:(在页面上加一个按钮,点击按钮显示Hello)
import javax.swing.*;
import java.awt.event.*;
public class SimpleGui implements ActionListener { //需要ActionListener接口
JButton button;
public static void main (String[] args) {
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go() {
...
button = new JButton("Click me"); //new一个Swing的按钮
button.addActionListener(this); //按钮绑定事件
...
}
//实现ActionListener接口内的actionPerformed方法,点击按钮会显示Hello
public void actionPerformed(ActionEvent event) {
button.setText("Hello");
}
}
上面这段代码可以达到目的,但显然不会有人这样去做。因为如果页面上需要多个按钮呢?你无法为接口里的actionPerformed方法提供多个实现。
比如,页面上有两个按钮,点击一个按钮显示hello,点击另一个按钮显示world:
public class SimpleGui implements ActionListener {
JButton button1; //两个按钮
JButton button2;
...
public static void main (String[] args) {
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go() {
...
button1 = new JButton("Click me"); //new一个Swing的按钮
button.addActionListener(this);
button2 = new JButton("Click me2"); //再new一个Swing的按钮
button2.addActionListener(this);
...
}
public void actionPerformed(ActionEvent event) {
//how???你无法为一个方法提供多个实现
}
}
或许你会这样来实现actionPerformed方法,以达到点击一个按钮显示hello,点击另一个按钮显示world的目的:
public void actionPerformed(ActionEvent event) {
if (event.getSource() == button1) {
button1.setText("Hello");
} else if (event.getSource() == button2) {
button2.setText("world");
}
}
但这样看起来太面向过程了,显得很屌丝,与你高富帅的气质形象极度不符。你真正需要的是内部类。
顾名思义,定义在一个类内部的类,就是内部类。它外层的类就是外部类。
内部类可以使用外部类的所有方法和变量,就算是外部类的private方法和变量也可以用。但内部类的方法和成员不能被外部类直接使用(即使内部类的public成员也无法被外部类直接访问),只能通过内部类对象来访问:
class MyOuterClass {
private int x;
class MyInnerClass { //定义在一个类内部的类,称为内部类
void go() { x = 42; } //没问题,外部类的private变量也能被内部类访问
}
}
定义完内部类,这样来创建内部类对象:
class MyOuter {
private int x;
MyInner inner = new MyInner(); //创建内部类实例,内部类实例一定会绑在外部类实例上
public void doStuff() {
//go(); //Error编译出错,外部类无法直接访问内部类内部的成员,哪怕是public也不行
inner.go(); //通过内部类对象才能调用内部类里的方法
}
class MyInner { //内部类定义处
void go() { x = 42; }
}
}
还有种创建内部类对象的方法,不太常见,但你可以了解下:
MyOuter outerObj = new MyOuter();
MyOuter.MyInner innerObj = outerObj.new MyInner();
原理是一样的,内部类的对象一定会绑在外部类对象上。创建外部类对象,用该外部类对象创建内部类对象。
例子代码改成:
public class SimpleGui {
JButton button1;
JButton button2;
...
public static void main (String[] args) {
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go() {
...
JButton button1 = new JButton("Click me");
button1.addActionListener(new buttonListener1());
JButton button2 = new JButton("Click me2");
button2.addActionListener(new buttonListener2());
...
}
class buttonListener1 implements ActionListener { //内部类1
public void actionPerformed(ActionEvent event) {
button1.setText("Hello");
}
}
class buttonListener2 implements ActionListener { //内部类2
public void actionPerformed(ActionEvent event) {
button2.setText("world");
}
}
}
上面都是自解释代码,应该能帮助你理解内部类的精妙:
内部类提供了在同一个类中对同一个方法,提供多种实现
PS:
上面这样将内部类作为外部类的成员来定义(即定义内部类和外部类自身的成员变量和 方法处以同一级别),被称为成员内部类。
除此之外还有局部内部类,匿名内部类,静态内部类。
局部内部类:定义在类的方法或任意作用域中
interface OutInterface { ... }
class OuterClass {
public OutInterface doit() {
class InnerClass implements OutInterface { //顾名思义,定义在外部类方法或任意作用域中的就是局部内部类
...
}
return new InnerClass();
}
public static void main(String args[]) {
OuterClass out = new OuterClass();
out.doit();
}
}
该局部内部类是doit方法的一部分,非OuterClass的一部分,因此doit方法外部无法访问该内部类。但内部类可以访问外部类成员(private成员)这一特性仍旧不变。
匿名内部类:没有类名的内部类
interface OutInterface { … }
class OuterClass {
public OutInterface doit(){
return new OutInterface(){ //顾名思义,无类名的称为匿名内部类(因为无类名,所以只能用默认构造函数)
...
};
}
public static void main(String args[]) {
OuterClass out = new OuterClass();
out.doit();
}
}
静态内部类:内部类前加上static就是静态内部类。
静态内部类中可以声明static成员,而非静态内部类中不可声明static成员。且静态内部类不可以使用外部类的非静态成员。且不需要外部对象来创建静态内部类对象:
public class OuterClass {
int x=0;
static class Inner{ //静态内部类
void doitInner(){
//x = 5; //编译Error,不能在静态内部类中访问外部类的非静态成员
}
public static void main(String args[]){ … } //可将外部类main函数,写入静态内部类
}
}
上面这样将外部类的static main函数,放入静态内部类后,编译将生成OuterClass.class和OuterClass$Inner.class。
执行java OuterClass$Inner就可以运行程序。这样当完成测试后,需要将.class文件打包时,只要删除OuterClass$Inner.class即可。
总结
无论什么内部类(成员,局部,匿名,静态)本质上仍旧是个类,具有普通类的一切特性。
但毕竟是定义在一个普通类的内部,生成访问权限受到一定限制(如只能通过外部类对象访问内部类),不能在程序中直接new一个内部类对象。内部类能访问外部类的一切成员(静态内部类有一定限制),但外部类无法直接访问内部类的成员,只能通过内部类对象来访问。
如果上面的说法比较难记,可以将外部类想象成丈夫,内部类想象成妻子。对妻子来说,你(丈夫)的就是我的,但我的仍旧是我的。外人无法直接拜访妻子,必须通过丈夫引见才能拜访到妻子。
内部类最强大之处在于:
能在同一个类中对同一个方法,提供多种实现