许多人认为内部类的语法十分复杂,尤其是匿名内部类,这与Java所一直奉行的“简单”原则相背离的,有人甚至怀疑java中加入这么一个“特征”(feature),是不是已经开始走向“灭亡”?就像许多其它语言一样走向“灭亡”?内部类是否真的有用,有没有存在的必要?我们首先来看看内部的工作原理。
先指明一点,内部类如何工作是由编译器来负责的,与java虚拟机无关,它对这个是一无所知的。仔细留意一下上篇中编译后产生的class文件,你会发现有一个class文件的名字是Court$TimerPrinter,它的基本格式是:外部类名称$内部类名称。当碰到内部类时,编译器会自动根据内部类的代码生成一个class文件并按照上述规则命名,那么编译器到底对它做了什么呢?我们可以使用Java的反射(reflection)机制来“偷窥”它,嘿嘿。具体的代码如下所示:
* Created on 2006-09-24
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package cn.edu.hust.cm.access;
import java.lang.reflect.*;
import javax.swing.*;
/**
* @author Demon
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class ReflectionTest {
public static void main(String[] args) {
String name="";
if(args.length>0)
name=args[0];
else
name=JOptionPane.showInputDialog("Class name (e.g. java.util.Date): ");
try{
Class c1=Class.forName(name);
Class c2=c1.getSuperclass();
System.out.print("class " + name);
if(c2!=null&&c2!=Object.class)
System.out.print(" extends " + c2.getName());
System.out.print("\n{\n");
printConstructors(c1);
System.out.println();
printMethods(c1);
System.out.println();
printFields(c1);
System.out.println("}");
}
catch(ClassNotFoundException e) { e.printStackTrace(); }
System.exit(0);
}
public static void printConstructors(Class c1){
Constructor[] constructors=c1.getDeclaredConstructors();
for(int i=0;i<constructors.length;i++){
Constructor c=constructors[i];
String name =c.getName();
System.out.print(Modifier.toString(c.getModifiers()));
System.out.print(" " + name + "(");
Class[] paramTypes = c.getParameterTypes();
for(int j=0;j<paramTypes.length;j++){
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printMethods(Class c1){
Method[] methods=c1.getDeclaredMethods();
for(int i=0;i<methods.length;i++){
Method m=methods[i];
String name=m.getName();
Class type=m.getReturnType();
System.out.print(Modifier.toString(m.getModifiers())+" "+type.getName()+" "+name+"(");
Class[] paramTypes=m.getParameterTypes();
for(int j=0;j<paramTypes.length;j++){
if(j>0) System.out.print(",");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printFields(Class c1){
Field fields[]=c1.getDeclaredFields();
for(int i=0;i<fields.length;i++){
System.out.print(Modifier.toString(fields[i].getModifiers()));
System.out.print(" ");
Class type=fields[i].getType();
System.out.print(type.getName());
System.out.println(" "+fields[i].getName()+";");
}
}
}
运行该程序,在对话框中输入cn.edu.hust.cm.test.Court$TimerPrinter,将会得到如下输出:
}
cn.edu.hust.cm.test.Court$TimerPrinter(cn.edu.hust.cm.test.Court);
public void actionPerformed(java.awt.event.ActionEvent);
final cn.edu.hust.cm.test.Court this$0;
}
如上所示,编译器自动为我们加上了一个域this$0,它指向一个外部类,另外自动给构造方法增加了一个Court型别参数,用来设置this$0的值,注意this$0是编译器自己合成的,不能直接引用。
既然编译器能够自动进行转化,为什么我们不直接自己进行转换,把TimerPrinter改写成普通的class呢?如下所示:
class Court
{
. . .
public void start()
{
ActionListener listener = new TimePrinter(this);
Timer t = new Timer(interval, listener);
t.start();
}
}
class TimePrinter implements ActionListener
{
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
. . .
private TalkingClock outer;
}
问题来了,我们在实现actionPerformed方法的时候要用到访问outer.beep,但是beep是private类型的,在TimerPrinter中是不能直接访问的。这样内部类的一个优点就显示出来了:内部类能够访问其所属外部类中的私有域而其它普通的类则不行。
那么内部类是通过什么样的机制访问它所属的外部类中的私有数据的呢?联想前面讲私有域的时候,我们都是通过方法来间接访问私有域的,那么这里是不是这样的呢?我们还是对外部类Court进行一下反射,结果如下所示:
class cn.edu.hust.cm.test.Court
{
public cn.edu.hust.cm.test.Court(int, boolean);
static boolean access$0(cn.edu.hust.cm.test.Court);
public void start();
private int interval;
private boolean beep;
}
我们看到新增了一个方法access$0,它的返回值就是传递过来的Court对象的beep域,这样actionPerformed方法中的if(beep)就相当于if(access$0(outer)),内部类就是通过这种机制来访问外部类的私有数据。