内部类(2):内部类与向上转型、在方法和作用域内的内部类

一、内部类与向上转型

    当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。(从实现了某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。)这是因为此内部类--某个接口的实现--能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。

    我们可以创建一个示例的接口:

/**
 * 目的地
 */
public interface Destination {
	// 读标签
	String readLabel();
}
/**
 * 内容
 */
public interface Contents {
	int value();
}

    现在Contents和Destination表示客户端程序员可用的接口。(记住,接口的所有成员自动被设置为public的。)

    当取得了一个指向基类或接口的引用时,甚至可能无法找出它确切的类型,看下面的例子:

class Parcel4 {
	private class PContents implements Contents {
		private int i = 11;

		@Override
		public int value() {
			return i;
		}
	}

	protected class PDestination implements Destination {
		private String label;

		private PDestination(String whereTo) {
			label = whereTo;
		}

		@Override
		public String readLabel() {
			return label;
		}
	}

	public Destination destination(String s) {
		return new PDestination(s);
	}

	public Contents contents() {
		return new PContents();
	}
}

public class TestParcel {
	public static void main(String[] args) {
		Parcel4 p = new Parcel4();
		Contents c = p.contents();
		Destination d = p.destination("Tasmania");
		// Parcel4.PContents pc= p.new PContents();
	}
}

    Parcel4中增加了一些新东西:内部类PContents是private,所以除了Parcel4,没有人能访问它。PDestination是protected,所以只有Parcel4及其子类,还有与Parcel4同一个包中的类(因为protected也给予了包访问权)能访问PDestination,其他类都不能访问PDestination。这意味着,如果客户端程序员想了解或访问这些成员,那是要受到限制的。实际上,甚至不能向下转型成private内部类(或protected内部类,除非是继承自它的子类),因为不能访问其名字,就像在TestParcel类中看到的那样。于是,private内部类各类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。此外,从客户端程序员的角度来看,由于不能访问任何新增加的、原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给java编译器提供了生成更高效代码的机会。

二、在方法和作用域内的内部类

    到目前为止,你所看到的只是内部类的典型用途。通常,如果所读、写的代码包含了内部类,那么它们都是“平凡的”内部类,简单并且容易理解。然而,内部类的语法覆盖了大量其他的更加难以理解的技术。例如,可以在一个方法里面或者在任意的作用域内定义内部类。这么做有两个理由:

  1. 如前所示,你实现了某类型的接口,于是可以创建并返回对其的引用。
  2. 你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。

    在后面的例子中,先前的代码将被修改,以用来实现:

  1. 一个定义在方法中的类。
  2. 一个定义在作用域内的类,此作用域在方法的内部。
  3. 一个实现了接口的匿名类。
  4. 一个匿名类,它扩展了有非默认构造器的类。
  5. 一个匿名类,它执行字段初始化。
  6. 一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)。

    第一个例子展示了在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类。这被称作局部内部类:

public class Parcel5 {
	public Destination destination(String s) {
		class PDestination implements Destination {
			private String label;

			private PDestination(String whereTo) {
				label = whereTo;
			}

			@Override
			public String readLabel() {
				return label;
			}
		}
		return new PDestination(s);
	}

	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Destination d = p.destination("Tasmania");
	}
}

    PDestination类是destination()方法的一部分,而不是Parcel5的一部分。所以,在destination()之外不能访问PDestination。注意出现在return语句中的向上转型--返回的是Destination的引用,它是PDestination的基类。当然,在destination()中定义了内部类PDestination,并不意味着一旦destination()方法执行完毕,PDestination就不可用了。

    你可以在同一个子目录下的任意类中对某个内部类使用类标识符PDestination,这并不会有命名冲突。

    下面的例子展示了如何在任意的作用域内嵌入一个内部类:

/**
 * 包裹6
 */
public class Parcel6 {
	// 内部跟踪
	private void internalTracking(boolean b) {
		if (b) {
			/**
			 * 跟踪
			 */
			class TrackingSlip {
				private String id;

				TrackingSlip(String s) {
					id = s;
				}

				String getSlip() {
					return id;
				}
			}
			TrackingSlip ts = new TrackingSlip("slip");
			String s = ts.getSlip();
		}
//		 TrackingSlip ts = new TrackingSlip("x");
	}

	// 跟踪方法
	public void track() {
		internalTracking(true);
	}

	public static void main(String[] args) {
		Parcel6 p = new Parcel6();
		p.track();
	}
}

    TrackingSlip类被嵌入在if语句的作用域内,这并不是说该类的创建是有条件的,它其实与别的类一起编译过了。然而,在定义TrackingSlip的作用域之外,它是不可用的;除此之外,它与普通的类一样。

如果本文对您有很大的帮助,还请点赞关注一下。

发布了100 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40298351/article/details/104259260