1内部类的创建和使用
(1)在外部类的非静态方法中使用内部类:与使用普通类没啥区别
(2)在外部类的非静态方法之外的任意位置创建某个内部类的对象,格式:OuterClassName.InnerClassName
public class Outer {
class Inner{
public void print(){
System.out.printf("我是内部类");
}
}
public Inner returnInner(){
return new Inner();
}
public static void main(String[] args) {
Outer o=new Outer();
Outer.Inner inner=o.returnInner();
inner.print();
}
}
//output:我是内部类
(3)内部类除了是一种名字隐藏和组织代码的模式。它还能访问其外围对象的所有成员而不需要任何特殊条件。此外,内部类还拥有其外部类的所有元素的访问权。
(4)在内部类中对外部类的引用.this
public class Outer {
void f() {
System.out.printf("Outer.f()");
}
public class Inner {
public Outer returnOuter() {
return Outer.this;
}
}
public Inner inner() {
return new Inner();
}
public static void main(String[] args) {
Outer outer = new Outer();
;
Outer.Inner oui = outer.inner();
oui.returnOuter().f();
}
}
(5)创建内部类对象必须使用外部类对象,如果创建静态内部类对象,则不用。例:
public class Outer {
public class Inner {
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner oui = outer.new Inner();
}
}
2内部类向上转型
内部类实现一个接口,访问修饰符设置为private或protected,能够隐藏实现细节。
public interface Contents {
int value();
}
class Parcel{
private class PContents implements Contents{
private int i=11;
public int value(){return i;}
}
public Contents contents(){
return new PContents();
}
public static void main(String[] args) {
Parcel p=new Parcel();
Contents c=p.contents();
}
}
3在方法和作用域内的内部类
使用情况:
(1)实现了某类型的接口,可以创建并返回对其的引用。
(2)要解决一个复杂的问题,想创建一个类来辅助解决方法,但又不希望这个类是公共可用的。
例1:定义在方法中的内部类。只在所定义的方法内可用。
public interface Destination {
String readLabel();
}
class Parcel{
public Destination destination(String s){
class PDestination implements Destination{
private String label;
public PDestination(String label) {
this.label = label;
}
@Override public String readLabel() {
return null;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel p=new Parcel();
Destination d=p.destination("abc");
}
}
注:PDestination类是destination()方法的一部分,而不是Parcel的一部分,所以在destination()之外不能访问PDestination
例2:在任意作用域内嵌入一个内部类
public class Parcel {
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();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel p = new Parcel();
p.track();
}
}
注:该内部类只在if语句作用域内可用。
4匿名内部类
例1:
interface Contents {
int value();
}
public class Parcel {
public Contents contents() {
return new Contents() {
private int i = 11;
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel p = new Parcel();
Contents c = p.contents();
}
}
例2:在匿名类中定义字段时还能够对其执行初始化操作:
interface Destination{
String readLabel();
}
public class Parcel {
public Destination destination (final String dest){
return new Destination() {
private String label=dest;
@Override public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel p=new Parcel();
Destination d=p.destination("abc");
}
}
注意:要想使内部类访问外部类的变量, 变量要加final
例3:在匿名类中不可能有命名构造器(因为它根本没有名字),但通过实例初始化,能够达到为匿名内部类创建一个构造器的效果。但是不能重载实例初始化方法,仅有一个这样的构造器。
abstract class Base{
public Base(int i){
System.out.printf("Base constructor,i="+i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i){
return new Base(i){
{
System.out.printf("Inside instance initializer");
}
public void f(){
System.out.printf("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base=getBase(47);
base.f();
}
}
注:此例中不要求变量一定是final的。因为i被传递给匿名类的基类的构造器,它并不会在匿名内部类被直接使用。
匿名内部类能继承类也可实现接口,但不能二者兼备,实现接口也只能实现一个接口。
例4使用匿名内部类的工厂方法
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementation1 implements Service{
private Implementation1() {
}
@Override public void method1() {
System.out.printf("Implementation1 method1");
}
@Override public void method2() {
System.out.printf("Implementation1 method2");
}
public static ServiceFactory factory=new ServiceFactory() {
@Override public Service getService() {
return new Implementation1();
}
};
}
class Implementation2 implements Service{
private Implementation2() {
}
@Override public void method1() {
System.out.printf("Implementation2 method1");
}
@Override public void method2() {
System.out.printf("Implementation2 method2");
}
public static ServiceFactory factory=new ServiceFactory() {
@Override public Service getService() {
return new Implementation2();
}
};
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact){
Service s=fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
serviceConsumer(Implementation2.factory);
}
}
5嵌套类(静态匿名内部类)
普通内部类对象隐式地保存了一个引用,指向创建它的外围类对象。
而嵌套类:(1)要创建嵌套类的对象,并不需要访问外围类的对象
(2)不能从嵌套类的对象中访问非静态的外围类对象
(3)普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但嵌套类可以包含所有这些东西。
例1:
interface Contents {
int value();
}
interface Destination {
String readLabel();
}
public class Parcel {
private static class ParcelContents implements Contents{
private int i=11;
public int value(){return i;}
}
protected static class ParcelDestination implements Destination{
private String label;
private ParcelDestination(String whereTo){label=whereTo;}
public String readLabel(){return label;}
public static void f(){}
static int x=10;
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("abc");
}
}
例2:接口的内部类
正常情况下不能在接口中放任何代码,但嵌套类可以作为接口的一部分。
接口中的任何类都自动地是public和static的。因为类是static的,只是将嵌套类置于接口的命名空间中,并不违反接口的规则。甚至可以在内部类中实现外围其接口。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
public void howdy(){
System.out.printf("howdy");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
如果想创建某些公共代码,使得它们可以被某个接口的所有不同实现所共有,那么使用接口内部的嵌套类会显得很方便。
例2从多层嵌套类中访问外部类成员
一个内部类被嵌套多少层并不重要,它能透明地访问所有它嵌入的外围类的所有成员。
class MNA{
private void f(){
}
class A{
private void g(){}
public class B{
void h(){
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna=new MNA();
MNA.A mnaa=mna.new A();
MNA.A.B mnaab=mnaa.new B();
mnaab.h();
}
}
可以看到在MNA.A.B中,调用方法g()和f()不需要任何条件(即使它们被定义为private)
6为什么需要内部类
interface Selector{
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] items;
private int next=0;
public Sequence(int size){
items=new Object[size];
}
public void add (Object x){
if(next<items.length)
items[next++]=x;
}
private class SequenceSelector implements Selector{
private int i=0;
public boolean end(){return i==items.length;}
public Object current(){return items[i];}
public void next(){if(i<items.length) i++;}
}
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence=new Sequence(10);
for(int i=0;i<10;i++){
sequence.add(Integer.toString(i));
}
Selector selector=sequence.selector();
while (!selector.end()){
System.out.printf(selector.current()+" ");
selector.next();
}
}
}
???如果Sequence不使用内部类,就必须声明Sequence是一个Selector,对于某个特定的Sequence只能有一个Selector。然而使用内部类很容易就能拥有另一个方法reverseSelector(),用它来生成一个反方向遍历序列的Selector。只有内部类才有这种灵活性
7闭包与回调
闭包:一个可调用对象,记录了一些信息,这些信息来自于创建它的作用域。
由此定义可以出内部类是面向对象的闭包。它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类可以操作所有成员包括private成员。
回调:通过回调对象能够携带某些信息,这些信息允许它在稍后的某个时刻调用初始的对象。
interface Incrementable{
void increment();
}
class Callee1 implements Incrementable{
private int i=0;
@Override public void increment() {
i++;
System.out.printf(i+"");
}
}
class MyIncrement{
public void increment(){
System.out.printf("Other Operation");
}
static void f(MyIncrement mi){mi.increment();}
}
class Callee2 extends MyIncrement{
private int i=0;
public void increment(){
super.increment();
i++;
System.out.printf(i+"");
}
private class Closure implements Incrementable{
public void increment(){
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbh){callbackReference=cbh;}
void go(){callbackReference.increment();}
}
public class Callbacks {
public static void main(String[] args) {
Callee1 c1=new Callee1();
Callee2 c2=new Callee2();
MyIncrement.f(c2);
Caller caller1=new Caller(c1);
Caller caller2=new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}