Java:内部类与接口的组合

1 内部类与接口组合的意义

我们都知道,内部类可以通过多种方式实现多重继承,而在这些方法中比较方便的就是配合接口实现多重继承,在达到这个目的外,这个方法还有一个额外的好处:可以通过向上转型(转为父类或者接口)来隐藏内部类的实现细节,仅仅得到其上层的一个引用,这在许多逻辑设计中都是适用的。

比如在设计时,将一辆车作为外围类,其各组件为内部类,组件都是通过接口来实现的,我们通过一定的方式让接口的引用来持有这些组件对象,就能对组件对象的“可见度”做很好的控制和保护,从而我们就无法对组件肆意访问或修改。

2 内部类与接口组合示例

2.1 一个简单的示例

我们将创建一个接口及实现它的内部类并对其测试

// 零件接口
interface Component { 
	String toString();
}

class Car  {
	// 创建“轮子类”来实现零件接口
	class Wheel implements Component {
		public String toString() { return "This is a Wheel."; }
	}
}

// 测试类
public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Car.Wheel cw = c.new Wheel();
		System.out.println(cw.toString());
	}
}
/* Output
This is a Wheel.
*/

注意
1 此处 Wheel 内部类前面没有访问权限修饰词,则默认为包访问权限,即包含 Car 类的包都能够访问这个内部类。
2 接口内部的方法默认为 public 类型。

2.2 通过接口完全隐藏内部类

要想完全隐藏内部类,只需要在内部类前面添加 private 访问权限即可。

interface Component { 
	String toString();
}

class Car  {
	// 创建“轮子类”来实现零件接口
	private class Wheel implements Component {
		public String toString() { return "This is a Wheel."; }
	}
}

// 测试类
public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Car.Wheel cw = c.new Wheel(); // 此行有错误
	}
}

当我们运行上述程序时,编译器报错如下:

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
	The type Car.Wheel is not visible
	The type Car.Wheel is not visible

错误消息:The type Car.Wheel is not visible(大意:Car 类的内部类 Wheel 类是不可见的)显示了两次,这说明无论是创建此内部类的引用(通过外部类类名访问内部类名称都不能!)或是创建内部类的对象,它都会出错,也就是说它对于外部(除去包含它的类)是完全隐藏的。接下来我会给出解决这一问题的最好途径。

上一篇博客中提到创建有关内部类的常用方法,其用来向外部环境传送内部类对象引用;此外我们还需要一个可以接受并持有此内部类对象的引用,达到此目的方法只有一个,就是创建”与 private 内部类有父子关系的接口或者类”的引用,即在外围类中创建一个返回内部类对象引用的方法。通过上述方法就可以解决如何在外部环境创建 private 内部类引用的问题。
解决方案示例如下:

// 零件接口
interface Component {
	String toString();
}

class Car  {
	// 创建“轮子类”来实现接口
	private class Wheel implements Component { // 添加 private 权限
		public String toString() { return "This is a Wheel."; }
	}
	// 返回内部类对象引用
	public Wheel getWheel() {
		return new Wheel();
	}
}

// 测试类
public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Component cw = c.getWheel(); // 引用声明为接口类型
		System.out.println(cw.toString());
	}
}
/* Output
This is a Wheel.
*/ 

注意
1 返回“private 内部类对象的引用”的方法不能声明为 private 类型,否则没有外部环境可以访问这个方法,从而无法得到内部类的对象引用。
2 ”与 private 内部类有父子关系的接口或者类”必须对特定情况是可见的,否则无法创建持有 private 内部类的引用。
3 向上转型可能会失去部分数据成员或者方法,关于这点要小心。

2.3 内部类与外围类可视度的不同

1.内部类可以可以看见外围类的任何成员,包括数据成员与方法。原因是:无论外围类的成员声明为 private、protected 还是包权限,它们都是满足“此类中可见的”,而内部类在外围类中,从逻辑上看它们也是外围类的“成员”,是它的一部分,所以它们互相可见。

2.外围类无法看见其内部类的任何成员(static 内部类要例外讨论,这种情况下可以通过内部类名来访问 static 内部类的 static 成员,但 static 内部类会丧失内部类的诸多优点,我们很少会使用到这个特性),即使内部类的成员声明为 public 类型。原因是:我们知道访问成员只有两种方法,

  • 1.通过 static 类名直接访问其 static 成员。
  • 2 通过类对象来访问成员。

我们在此不讨论第一种情况。第二种情况提到访问成员必须有对象,但在我们通过外部类对象的方法修改内部类成员时,此时内部类对象并没有被创建,这是违反 java 逻辑的,所以外围类不能访问(非 static)内部类的成员。

3 为什么要使用内部类?

据 Java 大牛 Bruce Eckel 所说,使用内部类最吸引人的原因是:”每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。”

根据上述言论,我们可以得到这样的结论:当遇到特定问题,比如在一个类里面实现多重继承时,接口解决了部分问题,而内部类有效的实现了“多重继承”(其不仅可以继承对象,还能够继承类或抽象类)。

注意:内部类并非是”所有可用以使用它的方案”中的第一选择,因为内部类的语法较复杂,如果普通的类可以满足要求,我们就应该使用普通的类而非内部类。比如,需求是要一个类继承单个接口(类或抽象类),这时我们只需要外围类进行继承就行,没必要用内部类去实现。

但是某些情况中,我们必须使用内部类。当我们需要在一个类中实现多重继承时,其继承的不仅仅只有接口,还包括抽象类或者具体类时,此时只靠外围类去继承它们就无法做到了(因为一个类不能同时继承接口和抽象类(或其他具体类)),我们还必须使用内部类去实现其它的接口(抽象类或具体类)。

4 总结

  1. 内部类特性使得 Java 的多重继承方案变得完整。
  2. 在使用内部类前要在设计阶段给出足够的理由,它并非总是第一选择。
  3. 接口是内部类最好的伙伴,它们的组合可以让内部类灵活得多。
  4. ”内部类看外围类的可视度”与“外围类看内部类的可视度”完全不同。

5 备注

更多细节可以看 Bruce Eckel 所著的《Java 编程思想》,本博文多为该书的学习心得,欢迎大家一起探讨。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值