Java编程思想: 内部类

内部类的基础知识 

内部类的定义

将一个类Inner的定义放在另一个类Outer的定义内部.  则Inner的具体类型为Outer.Inner

如果要引用Inner类型, 我们需要Outer.Inner, 即在类层次上, Inner是寄生于Outer的, 任何关于Inner的操作(如构造, 调用其方法)都需要通过Outer的实例对象生成一个Inner的对象(这样才能跟Outer.Inner类型关联起来)来进行操作.

public class Outer {
	public String s;
	Outer(String s) {
		this.s = s;
	}
	class Inner {
		public void show() {
			System.out.println(s);
			System.out.println("Inner show");
		}
	}
	public Inner inner() {
		return new Inner();
	}
	public static void main(String[] args) {
		Outer o = new Outer("???");
		Outer.Inner i = o.inner();
		i.show();
		System.out.println(o.s);
		
		// ERROR
//		Outer.Inner i1 = new Outer.Inner();

		Outer.Inner i1 = o.new Inner();
		i1.show();
	}
}

就像是任何类方法内部可以使用this来调用此类的所有成员一样. 内部类也同样使用隐式的"this指针"来访问外部类的所有成员,而不需要任何特殊条件(类似动态语言如Python,JavaScript的闭包原理).

.this与.new

如果要生成对外部类的引用, 需要.this

public class DotThis {
  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();
  }
}

这里不能使用return DotThis. 是因为DotThis是一个类, 而DotThis.this是一个引用对象,指的是当前内部类所引用的外部对象.

如果要创建内部类的对象, 则需要.new. 在拥有外部类对象之前不可能创建内部类对象的,这是因为内部类对象会暗暗的连接到创建它的外部类对象上.

public class DotNew {
  public class Inner {
    public void show() {
      System.out.println("Inner show");
    }
  }
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
    dni.show();
  }
}

 

内部类与向上转型

内部类的一个用途在于: 实现一个接口. 这样内部类可向上转型为一个接口的对象:

interface A {
  String toString();
}
public class C {
  private class B implements A {
    public String toString() {
      return getClass().getName();
    }
  }
  public static void main(String[] args) {
    C c = new C();
    C.B b = c.new B();
    System.out.println(b);
  }
}

这种设计符合组合思想,接口的实现类成为具体类的内部对象,从而很好的隐藏其实现细节.

 

内部类的使用

定义在方法中的类/定义在作用域中的类

定义在方法或作用域中的类, 主要为了解决以下情况: 方法/作用域的逻辑过于复杂, 我们需要创建一个类来辅助解决, 但又不希望这个类是公共可用的.

以下笔记主要基于下例几点:

1. 一个定义在方法中的类.

2. 一个定义在作用域内的类,此作用域在方法的内部.

3. 一个实现了接口的匿名类.

4. 一个匿名类,它扩展了有非默认构造器的类.

5. 一个匿名类,它执行字段初始化.

6. 一个匿名类,它通过实例初始化实现构造.

定义在方法中的类

interface A {
  String toString();
}
public class C {
  public A show() {
    class B implements A {
      public String toString() {
        return getClass().getName();
      }
    }
    return new B();
  }
  public static void main(String[] args) {
    C c = new C();
    // C$1B
    System.out.println(c.show());
  }
}

定义在作用域内中的内部类

interface A {
  String toString();
}
public class C {
  public String show(boolean b) {
    if (b) {
      class B implements A {
        public String toString() {
          return getClass().getName();
        }
      }
      A a = new B();
      return a.toString();
    } else {
      return "error";
    }
  }
  public static void main(String[] args) {
    C c = new C();
    System.out.println(c.show(true));
    System.out.println(c.show(false));
  }
}

 

匿名内部类

interface Contents {
  int value();
}
public class Parcel7 {
  public Contents contents() {
    return new Contents() {
      private int i = 11;
      public int value() { return i; }
    };
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents();
    System.out.println(c.value());
  }
}

对于匿名内部类的语法解析如下: new代表新建一个对象, 调用的是Contents()构造器, 其后增加的是类的实际定义.

备注: 这跟动态语言, 如Python/JavaScript的闭包一样.

上述的匿名内部类语法就是下述形式的简化形式:

interface Contents {
	int value();
}
public class Parcel7 {
	class MyCOntents implements Contents {
		private int i = 11;
		@Override
		public int value() {
			return i;
		}
	}

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

	public static void main(String[] args) {
		Parcel7 p = new Parcel7();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

 

如果匿名内部类使用了外部的参数, 那么其参数必须命名为final禁止被修改. 而在匿名类中同样可以使用实例初始化(即下例代码中大括号{}部分)来达到类似构造器的效果:

interface Contents {
  int value();
}
public class Parcel7 {
  public Contents contents(final int value) {
    return new Contents() {
      private int i;
      {
        i = value;
      }
      public int value() { return i; }
    };
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents(42);
    System.out.println(c.value());
  }
}

使用匿名内部类可以优化其工厂方法:

interface Service {
  void method1();
  void method2();
}
interface ServiceFactory {
  Service getService();
}
class Implementation1 implements Service {
  private Implementation1() {}
  public void method1() {
    System.out.println("Implementation1 method1.");
  }
  public void method2() {
    System.out.println("Implementation1 method2.");
  }
  public static ServiceFactory factory =
    new ServiceFactory() {
      @Override
      public Service getService() {
        return new Implementation1();
      }
    };
}
class Implementation2 implements Service {
  private Implementation2() {}
  public void method1() {
    System.out.println("Implementation2 method1.");
  }
  public void method2() {
    System.out.println("Implementation2 method2.");
  }
  public static ServiceFactory factory =
    new ServiceFactory() {
      @Override
      public Service getService() {
        return new Implementation2();
      }
    };
}
public class Factories {
  public static void serviceConsume(ServiceFactory fact) {
    Service s = fact.getService();
    s.method1();
    s.method2();
  }
  public static void main(String[] args) {
    serviceConsume(Implementation1.factory);
    serviceConsume(Implementation2.factory);
  }
}

 

嵌套类

使用static声明的内部类为嵌套类, 及它跟外围类的实例对象并没有任何的关联.

当内部类为static时,意味着:

1. 要创建嵌套类的对象, 并不需要其外围类的对象.

2. 不能从嵌套类的对象中访问非静态的外围类对象.

interface A {
  String toString();
}
public class C {
  public static class B implements A {
    public String toString() {
      return getClass().getName();
    }
  }
  public static void main(String[] args) {
    A a = new C.B();
    System.out.println(a);
  }
}

假设我们需要在接口中编写通用的公用代码, 用于不同接口实现的类所公用, 那么在接口中内嵌类是非常好的方法:

interface A{
  void howdy();
  class Test implements A{
    public void howdy() {
      System.out.println("Howdy");
    }
    public static void main(String[] args) {
      new Test().howdy();
    }
  }
}

public class ClassInInterface implements A{
  public void howdy() {}
  public static void main(String[] args) {
    A.Test t = new A.Test();
    t.howdy();
  }
}

我们通常在类中编写main来测试这个类. 如果嫌麻烦我们可以使用嵌套类来实现测试代码:

public class TestBed {
  public void f() {
    System.out.println("f()");
  }
  public static class Tester {
    public static void main(String[] args) {
      TestBed t = new TestBed();
      t.f();
    }
  }
}

执行: java TestBed$Tester即可测试.

 

为什么要使用内部类

主要原因在于: 每个内部类都能独立的继承自一个(接口的)实现, 所以无论外围类是否已经继承了某个(接口的)实现, 对于内部类都没有影响.

针对多重继承, 接口只解决了部分问题, 内部类使之得到完善.

考虑以下场景: 即必须在一个类中以某种方式实现两个接口. 这时候, 我们有两个选择: 要么使用单一类(全部implements两个接口), 要么使用内部类:

interface A {
	void show();
}
interface B {
	void func();
}

class X implements A, B {
	public void show() {
		System.out.println("X show");
	}
	public void func() {
		System.out.println("X func");
	}
}

class Y implements A {
	public void show() {
		System.out.println("Y show");
	}
	B makeB() {
		// 返回一个匿名类
		return new B() {
			public void func() {
				System.out.println("Y func");
			}
		};
	}
}
public class MultiInterfaces {
	static void takeA(A a) {
		a.show();
	}
	static void takeB(B b) {
		b.func();
	}
	public static void main(String[] args) {
		X x = new X();
		Y y = new Y();
		takeA(x);
		takeA(y);
		takeB(x);
		takeB(y.makeB());
	}
}

但如果拥有抽象类或具体类,而不是接口, 则只能使用内部类才能实现多重继承.

interface A {
	void show();
}
abstract class B {
	abstract void func();
}

class Y implements A {
	public void show() {
		System.out.println("Y show");
	}
	B makeB() {
		// 返回一个匿名类
		return new B() {
			public void func() {
				System.out.println("Y func");
			}
		};
	}
}
public class MultiInterfaces {
	static void takeA(A a) {
		a.show();
	}
	static void takeB(B b) {
		b.func();
	}
	public static void main(String[] args) {
		Y y = new Y();
		takeA(y);
		takeB(y.makeB());
	}
}

 

1. 内部类可以有多个实例, 每个实例都有自己的状态信息, 并且与其外围类对象的信息相互独立.

2. 在单个外围类中, 可以让多个内部类以不同的方式实现同一个接口, 或继承同一个类.

3. 创建内部类对象并不依赖于外围类对象的创建.

4. 内部类是独立的实体.

闭包与回调

闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域. 通过此定义,可以看出内部类是面向对象的闭包, 因为它不仅包含外围类对象(创建内部类的作用域)的信息, 还自动拥有一个指向此外围类对象的引用, 在此作用域内, 内部类有权操作所有的成员, 包括private成员.

内部类的继承

在继承内部类的时候, 由于内部类关联一个外部类的实例, 所以大概格式如下:

class WithInner {
	class Inner{}
}
public class InheritInner extends WithInner.Inner {
	InheritInner(WithInner wi) {
		wi.super();
	}
	public static void main(String[] args) {
		WithInner wi = new WithInner();
		InheritInner ii = new InheritInner(wi);
	}
}

内部类的覆盖

两个内部类是独立的两个实体,各自在自己的命名空间, 它们需要具体的外部类实例进行引用.

class Egg {
	private Yolk y;
	protected class Yolk {
		public Yolk() {
			System.out.println("Egg.Yolk()");
		}
	}
	public Egg() {
		System.out.println("New Egg()");
		y = new Yolk();
	}
}
public class BigEgg extends Egg {
	public class Yolk {
		public Yolk() {
			System.out.println("BigEgg Yolk()");
		}
	}
	public static void main(String[] args) {
		//New Egg()
		//Egg.Yolk()
		new BigEgg();
	}
}

局部内部类

我们可以在一个方法体的里面创建一个内部类,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,以及此外围类的所有成员.

下例对局部内部类与匿名内部类的创建进行了比较:

interface Counter {
	int next();
}
public class LocalInnerClass {
	private int count = 0;
	Counter getCounter(final String name) {
		class LocalCounter implements Counter {
			public LocalCounter() {
				System.out.println("LocalCounter()");
			}
			public int next() {
				System.out.print(name);
				return count++;
			}
		}
		return new LocalCounter();
	}

	Counter getCounter2(final String name) {
		return new Counter() {
			{
				System.out.println("Counter");
			}
			@Override
			public int next() {
				System.out.print(name);
				return count++;
			}
		};
	}

	public static void main(String[] args) {
		LocalInnerClass lic = new LocalInnerClass();
		Counter c1 = lic.getCounter("Local inner ");
		Counter c2 = lic.getCounter2("Anonymous inner");
		for (int i = 0; i < 5; i++)
			System.out.println(c1.next());
		for (int i = 0; i < 5; i++)
			System.out.println(c2.next());
	}
}

输出:

LocalCounter()
Counter
Local inner 0
Local inner 1
Local inner 2
Local inner 3
Local inner 4
Anonymous inner5
Anonymous inner6
Anonymous inner7
Anonymous inner8
Anonymous inner9

使用局部类而不是匿名内部类的唯一理由是: 我们需要一个已命名的构造器,或者需要重载构造器, 而匿名内部类只能用于实例初始化.

 

转载于:https://my.oschina.net/voler/blog/716600

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值