文章目录

  • 1 局部内部类
  • 1.1 局部内部类的定义
  • 1.2 使用局部内部类的理由
  • 1.3 局部内部类的使用示例
  • 1.3.1 方法中的局部内部类
  • 1.3.2 if 作用域中的局部内部类
  • 1.4 局部内部类的构造器
  • 1.5 经典用法
  • 1.6 局部内部类的可视度
  • 2 总结
  • 3 备注


1 局部内部类

1.1 局部内部类的定义

在一个方法里面或者在任意的作用域内定义的内部类就是”局部内部类”。

1.2 使用局部内部类的理由

  1. 我们实现了一个接口,于是可以创建并返回对其的引用(这个需求可以用更简单的方法实现,用的较少)。
  2. 当要解决了一个复杂的问题时,想创建一个类来当作辅助解决方案,但是又不希望这个类是公共可用的。

1.3 局部内部类的使用示例

我们将在方法、if 作用域中创建局部内部类,如下:

1.3.1 方法中的局部内部类

interface Component {
	void showLabel();
}

class Car  {
	// 返回 Component 接口类型的方法
	public Component getWheel (String s) {
		// 存在于方法中并且实现了 Component 接口的 Wheel 类
		class Wheel implements Component {
			private String label;
			private Wheel(String label) {
				this.label = label;
			}
			public void showLabel() { System.out.println(label); }
		}
		return new Wheel(s);
	}
}

public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Component co = c.getWheel("This is a wheel.");
		co.showLabel();
	}
}
/* Output
This is a wheel.
*/

解析:上述代码中,我们在外围类的 getWheel() 方法(返回 Component 接口类型)中创建了 Wheel 类(其实现了 Component接口),在方法末尾返回 Wheel 类的引用,然后此引用向上转型为 Component 类型,从而我们通过外围类的方法得到了局部内部类的对象引用,最后就可以在外部环境中使用此类。

当我们需要在外部持有局部内部类的对象时,我们必须使用一定的策略(此处是配合了接口,详情可以看下内部类与接口的组合)来实现这个目标。因为在局部内部类具体实现前,只有含有它的作用域可以访问它,所以外围类方法的返回类型想要使用其局部内部类就会报错。

有时我们并不需要在外部使用局部内部类,因为这个需求我们可以通过很多方法来实现,而非使用这个“不平凡”的内部类。当我们只需要在方法内使用局部内部类时,也就不需要向上转型来往外传递引用了。

1.3.2 if 作用域中的局部内部类

我们将只在”包含局部内部类的作用域”中创建其对象并使用,如下:

class People  {
	public void showMessages(boolean flag) {
		if(flag) {
			// Account 存在于 if 作用域中
			class Account {
				private String id;
				Account() { id = "ID: 810"; }
				void showId() { System.out.println(id); }
			}
			Account a = new Account();
			a.showId();
		}
		// 此处不能再访问 Account 类名称
		//! Account b = new Account();
	}
}

public class Test {
	public static void main(String[] args) {
		People p = new People();
		p.showMessages(true);
	}
}
/* Output
ID: 810
*/

解析:此处不再需要接口作为“在外部环境获得局部内部类对象”的辅助工具,我们直接在 if 作用域中声明类并使用即可。

注意:本例中,在 if 的作用域外就无法看见 Account 类了,原因是作用域的不同,从而导致不可见,Java 的作用域与 C 语言有很大的相似之处。

1.4 局部内部类的构造器

当内部类的构造器为 private 权限时,在我们创建了其对象后,就无法再通过构造器来重新初始化,这一点对局部内部类也适用。这个特点并非总是不可取的,只要符合设计,我们也可以通过可见的构造器来多次初始化对象,从而多次使用一个对象,省去了许多麻烦。

注意:多数内部类的构造器的特点一致,但匿名内部类除外,关于匿名内部类我会在另一篇博文中讲述。

1.5 经典用法

内部类的创建与使用中提到了有关内部类的经典用法:就是在外围类中创建一个方法,该方法创建返回该类的对象引用。当内部类要继承接口或类时,我们也可以通过局部内部类来实现上述需求了,虽然逻辑上稍变复杂,但是语法更为简单,示例如下:

interface Component {
	void showLabel();
}

class Car  {
	// 返回内部类对象引用的方法
	public Component getWheel (String s) {
		// 创建 Wheel 类实现 Component 方法
		class Wheel implements Component {
			private String label;
			private Wheel(String label) {
				this.label = label;
			}
			public void showLabel() { System.out.println(label); }
		}
		return new Wheel(s); // 创建对象并返回其引用
	}
}

public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Component co = c.getWheel("This is a wheel.");
		co.showLabel();
	}
}
/* Output
This is a wheel.
*/

解析:之前我们将内部类与返回它的对象引用的方法分开放置在外围类中,而本例中,我们将两者合二为一:首先让方法包含内部类,然后直接在方法末尾创建并返回该内部类的对象引用,从而可以在外部环境中通过外围类获得内部类的实体对象。

1.6 局部内部类的可视度

局部内部类不能有访问说明符(public、protected…),这是因为事实上局部内部类并不是外围类的一部分,虽然它可以访问当前代码块内的常量以及外围的所有成员。
下面将给出“含有访问说明符的局部内部类”的错误代码,如下:

interface Component {
	void showLabel();
}

class Car {
	public Component getWheel (String s) {
		public class Wheel implements Component { // 此行报错
			private String label;
			private Wheel(String label) {
				this.label = label;
			}
			public void showLabel() { System.out.println(label); }
		}
		return new Wheel(s);
	}
}

public class Test {
	public static void main(String[] args) {
		Car c = new Car();
		Component co = c.getWheel("This is a wheel.");
		co.showLabel();
	}
}

解析:上面的代码第七行会在运行时报错,错误信息是:
Illegal modifier for the local class Wheel; only abstract or final is permitted
根据上述的错误消息,可以知道 Wheel 类的修饰语(即 public)是非法的,它告知我们只能在类的头部使用 final 与 abstract 类型。

2 总结

  1. 局部内部类在使用前要有合适的理由
  2. 不同作用域下的局部内部类有不同的作用
  3. 局部内部类的经典用法可以同时代替普通内部类和返回它对象引用的方法
  4. 多数内部类的构造器特点一致,但匿名内部类除外