内部类之一

可以将一个类的定义放在另一个类的内部,这就是内部类,这篇文章我们将对内部类进行一个详尽的介绍。

在最初内部类看是一种代码隐藏机制,但是远不止如此,他了解外围并能与之通信。

1.创建内部类

创建内部类如你想想的一样,把类的定义放在外围类的里面

public class Clien {
	class Contents{
		private int i = 11;
		public int value(){
			return i;
		}
	}
	class Destination{
		private String label;
		public Destination(String whereto) {
			label = whereto;
		}
		String readLabel(){
			return label;
		}
	}
	public void ship(String dest)
	{
		Contents c = new Contents();
		Destination d = new Destination(dest);
		System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		Clien c = new Clien();
		c.ship("java");
	}
 
} 
outout:java
可以看到,在ship()里使用内部类与普通类没什么区别,只是内部类的名字嵌套在Clien里面,不过这不是唯一区别。
更典型的是外部类有一个方法返回对内部类的应用,就像to()和contents()方法那样。

public class parcel2 {
	class Contents{
		private int i = 11;
		public int value(){
			return i;
		}
	}
	class Destination{
		private String label;
		public Destination(String whereto) {
			label = whereto;
		}
		String readLabel(){
			return label;
		}
	}
	public Destination to(String s)
	{
		return new Destination(s);
	}
	public Contents contents()
	{
		return new Contents();
	}
	public void ship(String dest)
	{
		Contents c = new Contents();
		Destination d = new Destination(dest);
		System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		parcel2 p = new parcel2();
		p.ship("java");
		parcel2 p2 = new parcel2();
		//定义内部类的引用
		parcel2.Contents c = p.contents();
		parcel2.Destination destination = p2.to("java");
	}
}

如果想从外部类非静态方法之外的任意位置创建某个内部类的对象,那么就要像main()方法中那样,具体指明这个对象的类型,OutClassName.InnerClassName。但是在ship()中不用。

2.连接到外部类

内部类拥有外部类所有元素的访问权,不需要任何条件。
interface Selector{
	Object i = null;
	boolean end();
	Object current();
	void next();
}
public class Sequence {
	private Object[] items;
	private int next = 0;
	public Sequence(int size){ items = new Object[size];}
	public void add(Object x){
		if(next <items.length){
			items[next++] = x;
		}
	}
	private class SequenceSelector implements Selector{
		private int i = 0;		
		@Override
		public boolean end() {return i == items.length;}

		@Override
		public Object current() {return items[i];}

		@Override
		public void next() { if(i<items.length)i++;}		
	}
	public Selector selector(){
		return new SequenceSelector();
	}	
	public static void main(String[] args) {
		Sequence sequence = new Sequence(10);
		for(int i = 0;i < 10;i++){
			sequence.add(Integer.toString(i));
		}
		Selector selector = sequence.selector();
		while(!selector.end()){
			System.out.print(selector.current()+" ");
			selector.next();
			}		
	}
}

可以看到SequenceSelector这个内部类调用了外部类的方法和字段。内部类自动拥有对外部类所有成员的访问权限。

3.使用.this和.new

如果你需要生成对外围类对象的引用,可以使用外部类对象的名字加圆点和this。在编译时期受到检查,没有运行时开销。
public class DotThis {
	public void f(){System.out.println("DotThis.f()");}
	public class Inner{
		public DotThis Outer(){
			return DotThis.this;
		}
	}
	public Inner inner(){return new Inner();}
	public static void main(String[] args) 
	{
		DotThis dt = new DotThis();
		DotThis.Inner dti = dt.inner();
		dti.Outer().f();

	}
}
有时候你可能要告知某些其他对象,去创建某个内部类的对象。要实现此目的你必须在new表达式中提供对其他外部类对象的引用这时候需要使用.new语法
	public static void main(String[] args) 
	{
		DotThis dt = new DotThis();
		DotThis.Inner dtn = dt.new Inner();
		dtn.Outer().f();
	}

如代码所示。这就不需要上面的 inner()方法了直接用dt.new Inner();可以创建新的内部类。想要创建内部类对象你不能按照你想想的方式去引用外部类的名字DotNew,而是必须使用外部类对象来创建该内部类对象。所以你不能声明dn.new DotThis.Inner();。








在创建外部类对象之前是不可能创建内部类对象的,因为内部类对象是暗暗的连接到他的外部类对象上的。但是如果你创建的是嵌套类(静态内部类),那么就不需要对外部类对象的引用。
public class parcel3 {
	class Contents{
		private int i = 11;
		public int value(){return i;}
	}
	static class Destination{
		private String label;
		public Destination(String whereto) {label = whereto;}
		String readLabel(){return label;}
	}
	public static void main(String[] args) {

		parcel3 p = new parcel3();
		parcel3.Contents c = p.new Contents();
		//parcel3.Destination d = p.new Destination("java");
		parcel3.Destination d2 = new Destination("java");
	}
}
Destination是静态内部类在创建的时候直接是parcel3.Destination d2 = new Destination("java");没有外部类的引用。

4.内部类与向上转型

将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为内部类接口的实现,能完全不可见,并且不可用,所得到的只是指向基类或者接口的使用所以可以隐藏细节。
public interface Destination {
	String ReadLable();
}
public interface Contents {
	int value();
}
public class parcel4 {

	private class pContents implements Contents{
		private int i = 11;
		@Override
		public int value() {return i;}		
	}
	protected class pDestnation implements Destination{
		private String label;
		private pDestnation(String whereto){label = whereto;}
		public String ReadLable(){return label;}		
	}
	public Destination destnation(String s){
		return new pDestnation(s);
	}
	public Contents constents(){
		return new pContents();
	}

	public static void main(String[] args) {
		parcel4 p = new parcel4();
		Contents c = p.constents();
		Destination d = p.destnation("java");
		//非法
		//parcel4.Contents p2 = p.new pContents();
		
	}

}
内部类pContents是private所以除了parcel4能访问它没人能访问,pDestination时protected的所以访问权限受到约束,所以完全隐藏了实现的细节,而且扩展接口也没有了价值,编译器可以生成更高效的代码。

5.在方法和作用域内的内部类

前面都是内部类的典型应用。在方法和作用域里定义内部的原因是
1)如前所示,实现了某个接口,于是可以创建并返回对其引用。
2)你要解决一个复杂的问题,想创建一个类来解决方案,但是这个类不是公共的。
下面要修改先前的代码,以用来实现
1)一个定义在方法中的类。
2)一个定义在作用域中的类,此作用域在方法的内部。
3)一个实现了接口的匿名类。
4)一个匿名类,它扩展了有非默认构造器的类。
5)一个匿名类,它执行字段初始化。
6)一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)。
例1.局部内部类:在方法的作用域内。

public class parcel5 {

	public Destination destination(String s){
		class pDestination implements Destination{
			private String label;
			private pDestination(String whereto){label = whereto;}
			@Override
			public String ReadLable() {return label;}			
		}
		return new pDestination(s);
	}
	public static void main(String[] args) {
		parcel5 p = new parcel5();
		//Destination pd = new pDestination();非法
		Destination d = p.destination("java");
		System.out.println(d.ReadLable());
	}
}
在destination之外不能访问pDestination,当destination()方法执行完不代表pDestination不能用了。
例2:在任意作用域内嵌入一个内部类。
public class parcel6 {

	private void internalTracking(boolean b){
		if(b){
			class TrackingSlip{
				private String id;
				public TrackingSlip(String s) {
					this.id = s;
				}
				String getSlip(){return id;}
			}
			TrackingSlip ts = new TrackingSlip("slip");
			String s = ts.getSlip();
		}
		//这里不能用
		//TrackingSlip ts = new TrackingSlip("x");
	}

	public static void main(String[] args) {
		parcel6 p = new parcel6();
		p.internalTracking(true);

	}

}
可以看到在定义域之外是不可用的。

6.匿名内部类

下面的例子看起来有点奇怪了
public class parcel7 {
	public Contents constents(){		
		return new Contents() {
			private int i = 11;
			@Override
			public int value() {
				// TODO Auto-generated method stub
				return i;
			}
		};
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		parcel7 p = new parcel7();
		Contents c = p.constents();
	}
}
contents()方法将返回值的生成与表示这个返回值的类的定义结合在了一起,这么使用的都是抽象类或者接口,也可以是类,但是会当作接口处理。
我的理解是内部类主要是为了实现接口所以会有大括号{},是为了实现接口里面的方法。
它的简化形式是
public class parcel7b {
	class MyContents implements Contents{
		private int i = 11;
		@Override
		public int value() {
			// TODO Auto-generated method stub
			return i;
		}		
	}
	public Contents contents(){return new MyContents();}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		parcel7 p = new parcel7();
		Contents c = p.constents();
	}
}
在这个匿名内部类中使用了默认的构造器来生成Contents。下面代码展示如果你的基类需要一个有参数的构造器,怎么办
public class Wrapping {
	  private int i;
	  public Wrapping(int x) { i = x; }
	  public int value() { return i; }
}

public class parcel8 {
	public Wrapping wrapping(int x){
		return new Wrapping(x){
			public int value(){
				return super.value()*47;
			}
		};
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		parcel8 p = new parcel8();
		Wrapping w = p.wrapping(3);
		System.out.println(w.value());
	}
}
只需简单的传递合适得参数给基类构造器就可。这里将x传递给Wrapping(x),尽管Wrapping是一个普通类,但是还是被导出类当做公共“接口”来使用。
你还应该注意到Wrapping拥有一个要求传递一个参数的构造器。
在匿名类中定义字段时还能对其进行初始化操作。
public class parcel9 {
	public Destination destination(final String dest){				
		return new Destination(){
			private String label = dest;
			public String ReadLable() {return label;}			
		};		
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		parcel9 p = new parcel9();
		Destination d = p.destination("java");
		System.out.println(d.ReadLable());
	}
}
如果定义一个匿名内部类,并希望使用外部定义的一个对象,那么编译器要求其参数应用必须是final的。否则报错。
内部类不可能有命名构造器,通过用实例初始化达到为内部类创建构造器的 效果
abstract class Base{
	public Base(int i){
		println("Base Constructor i = "+i);		
	}
	public abstract void f();
}
public class AnonymousConstructor {

	public static Base getBase(int i){
		return new Base(i){
			{println("Inside instance initializer");}
			@Override
			public void f() {
				println("In anoymout f()");
			}
			
		};
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Base b = getBase(47);
		b.f();
	}

}
在这里i就不用final了,因为它不会被匿名内部类直接引用。
下面是才实例初始化的形式。
public class parcel10 {
	public Destination destination(final String dest,final float price){
		return new Destination(){
			private int cost;
			{
				cost = Math.round(price);
				if(cost>100)System.out.println("over budget");
			}
			private String label = dest;
			public String ReadLable() {	return label;}			
		};
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		parcel10 p = new parcel10();
		Destination d = p.destination("java", 101.33F);
	}
}
下面有具体的实例
1)
package inner_class;
import static net.lijun.util.Print.*;
interface  Service{
	void method1();
	void method2();
}
interface ServiceFactory{
	public Service getService();
}
class Implementation1 implements Service{
	private Implementation1(){}	
	public void method1() {
		// TODO Auto-generated method stub
		println("Implementation1 method1");
	}

	public void method2() {
		// TODO Auto-generated method stub
		println("Implementation1 method2");
	}
	public static ServiceFactory factory(){
		return new ServiceFactory() {			
			public Service getService() {
				// TODO Auto-generated method stub
				return new Implementation1();
			}
		};
		
	}
	
}
class Implementation2 implements Service{
	private Implementation2(){}	
	public void method1() {
		// TODO Auto-generated method stub
		println("Implementation2 method1");
	}

	public void method2() {
		// TODO Auto-generated method stub
		println("Implementation2 method2");
	}
	public static ServiceFactory factory(){
		return new ServiceFactory() {			
			public Service getService() {
				// TODO Auto-generated method stub
				return new Implementation2();
			}
		};
		
	}
	
}
public class Factories {

	public static void serviceConsumer(ServiceFactory factory){
		Service s = factory.getService();
		s.method1();
		s.method2();
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		serviceConsumer(Implementation1.factory());
		serviceConsumer(Implementation2.factory());
	}

}

2)
package inner_class;
import static net.lijun.util.Print.*;
interface Game{boolean move();}
interface GameFactory{Game getGame();}
class Checkers implements Game{
	private Checkers(){}
	private int moves = 0;
	private final int MOVES = 3;
	public boolean move() {
		println("Checkers move "+moves);
		return ++moves == MOVES;
	}
	public static GameFactory factor = new GameFactory() {
		public Game getGame() {
			// TODO Auto-generated method stub
			return new Checkers();
		}
	};
}
class Chess implements Game{
	private Chess(){}
	private int moves = 0;
	private final int MOVES = 4;
	public boolean move() {
		println("Checkers move "+moves);
		return ++moves == MOVES;
	}
public static GameFactory factor = new GameFactory() {
		public Game getGame() {
			// TODO Auto-generated method stub
			return new Chess();
		}
	};
}
public class Games {
	public static void PlayGame(GameFactory factory){
		Game s = factory.getGame();
		while(!s.move());
	}
	public static void main(String[] args) {
		PlayGame(Checkers.factor);
		PlayGame(Chess.factor);
	}
}
/*output:
Checkers move 0 
Checkers move 1
Checkers move 2
Checkers move 0
Checkers move 1
Checkers move 2
Checkers move 3*/
下次开始嵌套类
未完待续 微笑





















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值