10.1 创建内部类
把类的定义置于外围类的里面:
public class Parcel1 {
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel(){ return label;}
}
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");
}
}
当我们在ship()方法里面使用内部类时,与使用普通类没什么不同。在这里实际的区别只是内部类的名字是嵌套在类Parcel1里面。典型的情况,外部类将由一个方法,该方法返回一个指向内部类的引用,就像在to()和contents()方法看到的那样:
public class Parcel2 {
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel(){ return label;}
}
public Destination to(String s){
return new Destination(s);
}
public Contents contents(){
return new Contents();
}
public void ship(String dest){
Contents c = contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tasmania");
Parcel2 q = new Parcel2();
Parcel2.Contents c = q.contents(); //OuterClassName.InnerClassName
Parcel2.Destination d = q.to("Borneo");
}
}
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么就必须像在main()方法中那样,具体地指明这个对象的类型:OuterClassName.InnerClassName。
10.2 链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问 其外围对象的所有成员,而不需要任何特殊条件。此外内部类还拥有其外围类的所有元素的访问权。
下面的例子说明这一点:
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 s = new Sequence(10);
for (int i=0; i<10; i++)
s.add(Integer.toString(i));
Selector selector = s.selector();
while(!selector.end()){
System.out.print(selector.current()+" ");
selector.next();
}
}
}
这是迭代器设计模式的一个例子。
10.3 使用.this和.new
如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。这样产生的引用自动地具有正确的类型。
下例展示如何使用.this:
public class DotThis {
void f(){
System.out.println("Dotthis.f()");
}
public class Inner{
public DotThis outer(){
return DotThis.this; // this would be Inner's 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();
}
}
有时可能要告知某些其他对象,去创建某个内部类的对象。要实现此目的,必须在new表达式中提供对其他外部类对象的引用,这就需要使用.new语法,就像下面这样:
public class DotNew {
public class Inner{}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
要想直接创建内部类的对象,必须使用外部类的对象来创建内部类对象。因此不必声明(实际上不能声明)dn.new DotNew.Inner()。
在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地链接到创建它的外部类对象上。
10.4 在方法和作用域内的内部类
第一个例子展示在方法的作用域内创建一个完整的类。这被称作局部内部类。
public class Parcel5 {
public Destination destination(String s){
class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania");
}
}
PDestination类是destination()方法的一部分,而不是Parcel5的一部分。所以在destinnation()之外外不能访问 PDestination。注意return语句的向上转型--返回的是 Destination的引用,它是 PDestination的基类。
下面的例子展示如何在任意的作用域内嵌入一个内部类。
public class Parcel6 {
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();
}
//作用域之外不能用
//TrackingSlip ts = new TrackingSlip("x");
}
public void track(){ internalTracking(true);}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}
TrackingSlip 类被嵌入在if语句的作用域内,这并不是说该类的创建是有条件的,它其实与别的类一起编译过啦。然而,在定义TrackingSlip 的作用域之外,它是不可取的。除此之外,它与普通的类一样。
10.5 匿名内部类
public class Parcel7 {
public Contents contents(){
return new Contents() {
private int i = 11;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
上面的例子contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起!另外,这个类是匿名的,它没有名字。更糟的是,看起来似乎是你正要创建一个Contents对象。
创建一个继承自Contents的匿名类的对象。通过new表达式返回的引用被自动向上转型为对Contents的引用。
10.6 嵌套类
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。不能从嵌套类的对象中访问非静态的外围类对象。普通的内部类不能有static数据和static字段,也不能包含嵌套类,但是嵌套类可以包含这些东西。