Java内部类的高级用法

1、内部类链接到外部类

普通内部类可以访问外部类的一切,包括私有成员与方法。示例:

public class Tree {
	class MyRoot implements Interface.Root {
		@Override
		public void show() {
			System.out.println("Root of " + NAME);
		}
	}

	private String NAME = "Tree";
	private MyRoot r = new MyRoot();

	void show() {
		r.show();
	}

	public static void main(String[] args) {
		Tree t = new Tree();
		t.show();
		
		// 这样子不可以
		// Interface.Root r = new Tree.MyRoot();
		
		// 这样子可以
		Interface.Root x = t.new MyRoot();
		x.show();
	}
}

可以看到,普通内部类MyRoot访问了外部类Tree的私有成员”NAME”,天然就可以,不需要额外的什么语法。

Interface.Root r = new Tree.MyRoot()这句话是不可以的,因为MyRoot中的show方法访问了外部类的私有成员NAME,它的存在依赖于外部类的存在,因此不允许单独创建普通内部类。普通内部类的创建要依赖于外部类:Interface.Root x = t.new MyRoot();
这样,当调用x.show()时,show()方法就知道要访问”t”这个实例中的NAME属性,而不是其它。

2、用内部类实现集合类迭代器

普通内部类作为可以独立使用的类,拥有访问外部类一切成员与方法的特性,类似与C++中的友元类,在某些情景下,有不可代替的作用。如实现集合类的迭代器,示例如下:

package com.zhangxf.test;

interface Selector {
	boolean end();

	Object current();

	void next();
}

public class Sequence {
	private Object[] o;
	private int next = 0;

	public Sequence(int size) {
		o = new Object[size];
	}

	public void add(Object x) {
		if (next < o.length) {
			o[next] = x;
			next++;
		}
	}
	
	private class SSelector implements Selector {
		int i = 0;
		
		public boolean end () {
			return i == o.length;
		}
		
		public Object current () {
			return o[i];
		}
		
		public void next () {
			if (i < o.length) i ++;
		}
	}
	
	public Selector getSelector() {
		return new SSelector();
	}

	public static void main(String[] args) {
		Sequence seq = new Sequence(10);
		for (int i = 0; i < 10; i ++) {
			seq.add(i);
		}
		Selector sel = seq.getSelector();
		while (!sel.end()) {
			System.out.println((int)sel.current());
			sel.next();
		}
	}

}

上例中,Sequence类是一个集合类,它只有一个构建函数与一个add()方法,很简单。同时通过私有普通内部类,实现了Selector接口,Selector接口是一个迭代器。Sequence类另外提供了一个返回内部类句柄的getSelector()方法。

可以看到,任何集合类都可以实现Selector接口。
我们可以通过Selector接口,以统一的方式访问任何实现了Selector接口的集合类,只所以能这样,是因为使用了普通内部类实现Selector接口,而普通内部类对外部类拥有所有的访问权限。
同时,每个返回的Selector实例都有自己的状态,因为普通内部类也可以有自己的成员

当然,内部类的这个特性不只用来实现迭代器,这里只是展示他的特性。

3、静态内部类

静态内部类在形式上与普通内部类的区别是在定义时在前边加上static限定符。静态内部类有很多限制,因为它是静态的,并不属于某个外部态实例,而是属于整个外部类,因此静态内部类不可访问外部类的非静态成员也方法。另外,静态内部类不可以有自己的静态成员与静态内部类,但可以有静态方法。

静态类有特殊的用处。在写某个类时,一般会同时在类中加入main()方法用于执行测试逻辑,这样结果就是测试代码与将来要发布的代码混合在一起,显然开发者并不希望将自己的测试代码发布出去。另外测试代码分布在各个文件中,非常分散不易于管理。使用静态内部类,可以将所有测试代码集中到一个文件中,实现测试代码与发布代码的分离,这样发布的时候就可以过滤掉测试代码,示例如下。
不使用静态内部类时:

//////////////////////////////////文件A.java///////////////////////////////////
public class A {
	
	void show( ) {
		System.out.println("I am class A");
	}

	public static void main(String[] args) {
		A a = new A();
		a.show();
	}

}
///////////////////////////////////文件B.java//////////////////////////////////
public class B {
	
	void show() {
		System.out.println("I am class B");
	}

	public static void main(String[] args) {
		B b = new B();
		b.show();
	}

}

测试代码分布在A.java与B.java中,并且会随代码一起发布。

使用静态内部类后:

//////////////////////////////////文件A.java///////////////////////////////////
public class A {
	
	void show( ) {
		System.out.println("I am class A");
	}
}
///////////////////////////////////文件B.java//////////////////////////////////
public class B {
	
	void show() {
		System.out.println("I am class B");
	}

}
///////////////////////////////////文件Tester.java////////////////////////////
public class Tester {
	public static class TestA {
		public static void main(String[] args) {
			A a = new A();
			a.show();
		}
	}
	public static class TestB {
		public static void main(String[] args) {
			B b = new B();
			b.show();
		}
	}

}

测试代码集中在Tester.java中,可以控制它不发布。内部静态类TestA会编译成Tester$TestA.class,TestB也一样的结果。像JUNIT之类的测试框架可能用到了这种机制。

利用静态内部类实现单例模式:

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

4、内部类覆盖

假如有一个类A,它定义了内部类X,我们从A派生出一个新类B,并在B内重新定义X,这样B内的X就会覆盖A内X的实现,从而生新一个新类,其它一切不变。这个想法看起来有用,实际上行不通。测试代码如下:

class Egg {
	protected class Yolk {
		public Yolk() {
			System.out.println("Egg.Yolk()");
		}
	}
	private Yolk y;
	public Egg() {
		System.out.println("New Egg()");
		y = new Yolk();
	}
}

public class BigEgg extends Egg {
	public class Yolk {
		public Yolk() {
			System.out.println("BigEgg.Yolk()");
		}
	}
	public static void main(String[] args) {
		BigEgg x = new BigEgg();
		BigEgg.Yolk y = x.new Yolk();
		Egg.Yolk z = ((Egg)x).new Yolk();
	}

}

输出如下:
New Egg()
Egg.Yolk()             // 仍然是Egg中的Yolk而非派生类的BigEgg.Yolk。
BigEgg.Yolk()        //  这个是main中的第二行代码输出的内容,依托第一行创建的实例,生成了BigEgg中Yolk的实例
Egg.Yolk()             //  第三行代码的输出,依托于第一行创建的实例,但必需先向上转换成Egg,生成Egg中Yolk的实例

可以看到Egg中Yolk的实例定义方式是Egg.Yolk,而BigEgg中的Yolk定义方式是BigEgg.Yolk,后者并没有覆盖前者。

5、内部类继承

假如有一个类A,它定义了内部类X,我们从A派生出一个新类B,并在B内重新定义X,但是B中的X继承A中的X,这个可以。但要注意刚才说的覆盖问题,A中的X与B中的X是两个不同的东西,后者并不会覆盖前者:
 

package com.zhangxf.test;

class Egg {
	protected class Yolk {
		public Yolk() {
			System.out.println("Egg.Yolk()");
		}
		
		public void show () {
			System.out.println("Egg.show()");
		}
	}
	private Yolk y;
	public Egg() {
		System.out.println("New Egg()");
		y = new Yolk();
	}
	
	public void show () {
		y.show();
	}
}

public class BigEgg extends Egg {
	public class Yolk extends Egg.Yolk {
		public Yolk() {
			System.out.println("BigEgg.Yolk()");
		}
		
		public void show () {
			System.out.println("BigEgg.show()");
		}
	}
	public static void main(String[] args) {
		Egg x = new BigEgg();
		x.show();
	}

}

BigEgg的内部类Yolk继承了Egg的内部类,但是后者的内部类仍然不会覆盖前者的内部类。main代码中,将BigEgg向上转型成Egg,然后调用其方法show(),显示内容仍然是Egg中的Yolk实现而不是BigEgg中的Yolk。输出内容如下:
New Egg()
Egg.Yolk()
Egg.show()
 

    原文作者:五星上炕
    原文地址: https://blog.csdn.net/dkfajsldfsdfsd/article/details/84938143
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞