Effective Java中嵌套类定义:嵌套类(nested class)是指被定义在另一个类的内部的类。嵌套类存在的目的应该只是为它的外围类(enclosing class)提供服务。
嵌套类有四种:静态成员类(static member class)、非静态成员类(nonstatic member class)、匿名类(anonymous class)和局部类(local class)。除了静态成员类之外,其他的都被称为内部类(inner class)。
静态成员类(嵌套类)
嵌套类:
- 要创建嵌套类的对象,并不需要其外围类的对象
- 不能从嵌套类的对象中访问非静态的外围类对象
普通内部类的字段与方法,只能放在类的外部外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含以上。
public class Parcel11 {
private static class ParcelContents implements Contents {
private int i = 11;
@Override
public int value() {
return i;
}
}
protected static class ParcelDestination implements Destination {
private String label;
private static int i = 9;
private ParcelDestination(String whereTo) {
label = whereTo;
}
@Override
public String readLable() {
return label;
}
/**
* 嵌套类可以包含其他静态元素
*/
public static void f() {
}
static class AnotherLevel {
public static void f() {
}
static int x = 10;
}
}
public static Destination destination(String s) {
return new ParcelDestination(s);
}
public static Contents contents() {
return new ParcelContents();
}
public static void main(String[] args) {
Contents c = contents();
Destination d = destination("Tasmania");
}
}
非静态成员类(成员内部类)
public class Parcel1 {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
private class Destination {
private String label;
public Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
class DestinationInner{
}
}
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) {
Parcel1 p = new Parcel1();
p.ship("Tasmania");
}
}
内部类的访问修饰符可以是private、protected、prublic、默认。
匿名类
常见用法:
- 实现接口的匿名类
- 扩展了有非默认构造器的类
- 执行字段初始化
- 通过实例初始化实现构造
实现接口的匿名类
示例:
interface Contents {
int value();
}
public class Parcel7 {
public Contents contents() {
return new Contents() {
private int i = 10;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
扩展了有非默认构造器的类
示例:
class Wrapping {
private int i;
public Wrapping(int i) {
this.i = i;
}
public int value(){
return i;
}
}
public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) {
@Override
public int value() {
return super.value() * 47;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(10);
System.out.println(w.value());
}
}
赋值和实例初始化实现构造
简单赋值效果,示例:
interface Destination {
String readLable();
}
public class Parcel9 {
// JDK8以下方法中的参数需要final修饰
public Destination destination(final String dest){
return new Destination() {
private String label = dest;
@Override
public String readLable() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("yy");
}
}
通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。示例:
abstract class Base {
public Base(int i) {
System.out.println("Base construct,i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i){
return new Base(i) {
// 用于匿名内部类的初始化
{
System.out.println("Inside instance initializer");
}
@Override
public void f() {
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}
局部内部类
常见用法:
- 在方法的作用域内创建一个完整的类
- 代码块中
在方法的作用域内创建一个完整的类
示例:
interface Destination {
String readLable();
}
public class Parcel5 {
private int i = 5;
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
System.out.println(i);
// 使用外部类的i
System.out.println(Parcel5.this.i);
}
@Override
public String readLable() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("yy");
}
}
注意事项:
- PDestination类是destination( )方法的一部分,而不是Parcel5的一部分。所以只能在destination( )方法体中使用。
- 局部内部类访问外部类的成员使用:外部类名.this.成员名(Parcel5.this.i)。
- 不能加访问修饰符,因为不是类成员
- 局部内部类访问作用域内的局部变量,该局部变量需要使用final修饰(适用于JDK8以下)
- 当需要一个已命名的构造器或者需要重载构造器
- 使用局部内部类而不使用匿名内部类的另一个理由是,需要不止一个该内部类的对象
使用内部类的原因
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
内部类使得多重继承的解决方案变得完整。
使用内部类可以获得的其他一些特性:
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
- 创建内部类对象的时刻并不依赖于外围类对象的创建。
- 内部类并没有令人迷惑的“is-a”关系;它就是一个独立的实体。
内部类的继承
实例1:
class WithInner {
class Inner {
}
}
public class InheritInner extends WithInner.Inner {
/**
* 当要生成一个构造器时,默认的构造器不能只是传递指向外围类对象的引用。必须在构造器内这样处理,程序才能编译通过。
* @param wi
*/
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
实例2:
class FirstOuter {
FirstOuter(){
System.out.println("Hello");
}
public class FirstInner {
FirstInner(String s) {
System.out.println("FirstOuter.FirstInner() " + s);
}
}
}
public class SecondOuter {
public SecondOuter() {
System.out.println("World");
}
public class SecondInner extends FirstOuter.FirstInner {
SecondInner(FirstOuter x) {
x.super("hello");
System.out.println("SecondOuter.SecondInner()");
}
}
public static void main(String[] args) {
FirstOuter fo = new FirstOuter();
SecondOuter so = new SecondOuter();
SecondInner si = so.new SecondInner(fo);
}
}
内部类覆盖
class Egg2 {
public Egg2() {
print("New Egg2");
}
protected class Yolk {
public Yolk() {
print("Egg2.Yolk()");
}
public void f() {
print("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
print("BigEgg2.Yolk()");
}
@Override
public void f() {
print("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
//Egg2 e = new Egg2();
}
}
output:
Egg2.Yolk()
New Egg2
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()