从C#到Java入门指引之三——内部类

C#和Java对static关键字的理解有很大分歧,主要就是在内部类上。此外,由于Java没有类似委托这种数据结构,内部类还要担当封装方法和响应事件这样的重要责任。

从C#到Java入门指引之一——基本类型和字符串
从C#到Java入门指引之二——类
从C#到Java入门指引之三——内部类

截然不同的内部类

与C#不一样,Java不允许外部类使用static关键字修饰。那么我们来看看static关键字修饰内部类的一个例子(来自《细说Java》)

public class Circle {
    private static int radius;
    public static class Center{
        // 静态内部类也可声明静态成员
        private static Center defaultCenter;
        static {
            defaultCenter = new Center(10, 10);
            // 访问外部类的私有成员
            System.out.println(radius);
        }
 
        public int x, y;
        public Center(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
}
 
public class Main{
    public static void main(String[] args){
        // 静态内部类可以不依赖外部来实例的存在而存在
        Circle.Center center = new Circle.Center(15, 20);
    }
}

习惯C#的同学只怕又会觉得纳闷,明明这个内部类都static了,为啥还有构造方法?!
这是两种语言对static的不同解读造成的:
在C#中,static修饰内部类,表示这是个不能够实例化的静态类;而在Java中,这表示这个内部类可以不依赖于外部类的实例而实例化。

所以,Java中加了static的内部类相当于C#中普通的内部类。
那么,没加static修饰的内部类,又表示什么呢?自然是依赖于外部类的实例才能实例化的内部类嘛!

public class Circle {
    private int radius;
    public class Center{
        // 普通内部类也可声明静态成员
        //private static Center defaultCenter;
 
        // 但是可以含有静态常量
        private static final int FINAL_STATIC = 10;
 
        public int x, y;
        private int radius;
        public Center(int x, int y){
            this.x = x;
            this.y = y;
            radius = 3; // 访问内部类的成员
            this.radius = 3; // this指向内部类
            Circle.this.radius = 3; // Circle.this指向外部类
        }
    }
}
 
public class Main{
    public static void main(String[] args){
        // 普通内部类必须依赖外部类的实例
        //Circle.Center center = new Circle.Center(15, 20);
 
        // 必须先创建外部类的实例
        Circle circle = new Circle();
        Circle.Center center = circle.new Center(15, 20);
    }
}

看到创建内部类实例的那行代码,是否觉得很别扭?
没错,而且如果又涉及到继承的话,比如继承某个类的内部类,那就更加繁琐了。所以我的建议是,非static的内部类,能不用就不用。

方法内声明的局部类

普通内部类可以在语句块(比如方法)内声明,那就是局部类。
局部类只在当前语句块内可访问。
局部类可以访问语句块中的局部变量(但必须用final修饰),以及外部类中的成员。

更为常用的匿名类

如果局部类只是用一次,就可以声明为匿名类。例子同样来自《细说Java》:

interface Center{}
 
public class Circle {
    public Center getCenter(final int pointX, final int pointY){
        return new Center(){
            public int x, y;
            {
                x = pointX;
                y = pointY;
            }
        };
    }
}
 
public class Main{
    public static void main(String[] args){
        Circle circle = new Circle();
        Center center = circle.getCenter(10, 15);
    }
}

匿名类不能声明构造方法,所以只能用初始化块的方式对成员初始化。
匿名类也不是new后面的那个类型:如果new后面是一个类,那么匿名类的类型是这个类的子类;如果new后面是一个接口,那么匿名类的类型是这个接口的一个实现。

比如我们常常会给Activity添加一个Handler,以处理各种消息。每个Activity的Activity各不相同,所以也就没有重用的必要,用匿名类即可:

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        //...
    }
};

匿名类用于封装方法

许多时候,我们需要把方法当成一个变量处理,传给另一个方法。C/C++可以用函数指针,C#可以用委托。Java没有比较直观的方式,但一般可以这么做:
1.创建接口IFunc,里面放一个方法的原型,比如void func();
2.创建实现了IFunc的类FuncClass,实现其中的func方法;
3.创建类FuncClass的对象funcObject,传递给别的方法。

很麻烦是不,好在有匿名类,可以将创建类和创建对象合成一步。比如,想要把一段代码放到新线程去跑,就得把代码塞到很麻烦是不,好在有匿名类,可以将创建类和创建对象合成一步。
比如,想要把一段代码放到新线程去跑,就需要创建Runnable接口的实现,把代码塞到run方法,传给Thread的构造方法:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        //...
    }
});
thread.start();

注:实际使用时,只需要直接重写Thread的run方法,这么写其实是绕了弯路。

实现那个方法人们会把需要传递的方法放在一个类里面,然后用这个类创建一个对象,通过传递这个对象来实现传递方法。这时候,匿名类就可以大显身手了。

匿名类与事件响应机制

许多时候,我们需要把方法当成一个变量处理,传给另一个方法。C/C++可以用函数指针,C#可以用委托,但Java没有比较直观的方式。通常,在Java代码中,人们会把需要传递的方法放在一个类里面,然后用这个类创建一个对象,通过传递这个对象来实现传递方法。这时候,匿名类就可以大显身手了。

最简单的例子,响应按钮的点击事件:

mButton.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View view){
        //...
    }
}

缺省为静态的内部接口

声明在类内部的接口,即为内部接口。
内部接口缺省就是静态的————这个也容易理解,既然是接口,难道还要依赖于外部类的实例而存在么。

 

 

posted on 2013-01-09 11:51  nuliniao  阅读(820)  评论(0编辑  收藏  举报