内部类
- 实例
Outer.Inner in = new Outer().new Inner();
-
必须有外部类的对象来创建内部类,因为内部类创建时会自动进行绑定
-
内部类编译后会产生单独的class文件,如
外部类$内部类.class
-
内部类可以声明为public、private、protected、default
-
内部类中, 访问外部类成员 : 直接访问, 包括私有(无论嵌套多少层都可以透明的访问,创建时也需要逐级
.
) - 通常直接访问就可以,如果需要显示访问:格式
Outer.this.
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;
@Override
public boolean end() { return i == items.length; }
@Override
public Object current() { return items [i]; }
@Override
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.print(selector.current() + " ");
selector.next();
}
}
}
内部类分类¶
[[内部类的三种类型]] - 局部内部类 - 在方法作用域中创建内部类
public class Parcel5 {
public Destination destination(String s) {
//在返回方法中定义一个内部类,这个内部类只能在该方法作用域中使用,他的定义不能在外部使用,但是可以向上转型后作为返回值返回
final class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
@Override
public String readLabel() { return label; }
}
return new PDestination(s);
}
public static void main(String [] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania");
}
}
- 匿名内部类:定义一个类的同时对其进行实例化
- 匿名类只能扩展一个类或实现一个接口
public class Parcel7 { public Contents contents() { return new Contents() { // Insert class definition private int i = 11; @Override public int value() { return i; } }; // Semicolon required } public static void main(String [] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); } }
-
创建了一个继承自Contents的匿名类对象,返回会自动向上转型
-
也可以通过构造函数传参,可以用于调用超类的构造函数
public class Parcel8 { public Wrapping wrapping(int x) { // Base constructor call: return new Wrapping(x) { // [1] @Override public int value() { return super.value() * 47; } }; // [2] } public static void main(String [] args) { Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); } } public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } }
-
如果要在匿名类使用匿名类外的对象,要求参数用final 修饰,或者保证初始化后就不会再变化
- 此规则的背后是闭包(Closure)的概念,在Java中,匿名类或lambda表达式可以捕获外部作用域中的变量,这些变量被称为捕获变量。为了保证变量在被捕获时的值在使用时依然有效并且不被意外修改,Java要求这些变量必须是
final
或“effectively final”。
- 此规则的背后是闭包(Closure)的概念,在Java中,匿名类或lambda表达式可以捕获外部作用域中的变量,这些变量被称为捕获变量。为了保证变量在被捕获时的值在使用时依然有效并且不被意外修改,Java要求这些变量必须是
-
匿名函数的构造器
-
可以使用初始化块
{}
public class Parcel10 { public Destination //被使用的对象要保证为 final destination(final String dest, final float price) { return new Destination() { private int cost; // 初始化块 { cost = Math.round(price); if(cost > 100) System.out.println("Over budget!"); } private String label = dest; @Override public String readLabel() { return label; } }; } public static void main(String [] args) { Parcel10 p = new Parcel10(); Destination d = p.destination("Tasmania", 101.395F); } }
-
静态内部类(嵌套类),内部类使用static修饰,
- 不需要内部类对象和外部类对象之间的连接
- 不需要通过外部类对象创建
- 无法从内部访问非static的外部对象
- 嵌套类内可以有static的数据及字段
Outer.Inner in = new Outer.Inner();
- 可以定义在接口中,默认就是public static的
- 静态内部类不是真的“静态”,只是不依赖于外部类
- 也可以有非静态方法,并且可以创建多个实例
- 静态内部来与外部顶层类是类似的,只是定义在一个类的内部
应用¶
- 间接实现多继承
- 每个内部类都有可以独立地继承自一个实现
-
可以使用每个内部类去继承一个类,间接实现多继承
class D {} abstract class E {} class Z extends D { E makeE() { return new E() {}; } } public class MultiImplementation { static void takesD(D d) {} static void takesE(E e) {} public static void main(String [] args) { Z z = new Z(); takesD(z); takesE(z.makeE()); } }
-
闭包与回调 ^44cea9
interface Incrementable { void increment(); } //直接实现接口 class Callee1 implements Incrementable { private int i = 0; @Override public void increment() { i++; System.out.println(i); } } //实现方法,但没有实现接口 class MyIncrement { public void increment() { System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); } } //使用内部类实现接口,并给出方法用于获取内部接口(这里的内部接口会对外部进行访问,是一个闭包) class Callee2 extends MyIncrement { private int i = 0; @Override public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { @Override public void increment() { // Specify outer-class method, otherwise // you'll get an infinite recursion: 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(); } }
-
闭包:允许方法保留和使用定义在其外部作用域的变量,即使外部方法已经完成执行。
Callee2
的内部类Closure
类似于一个闭包,因为它封装了Callee2
的环境并提供了对其increment
方法的访问。 - 闭包让你可以在一个内层函数中访问到其外层函数的作用域。 [[什么是闭包]]
- 回调:一个方法接受另一个方法作为参数,然后在适当的时候执行传递。
Caller
类接受一个实现了Incrementable
接口的对象作为回调,并在go
方法中调用该回调对象的increment
方法。