java炒冷饭系列11 方法和作用域内的内部类 与 匿名内部类
来源:互联网 发布:淘宝雪花代码在线生成 编辑:程序博客网 时间:2024/06/11 18:42
在方法和作用域内的内部类
到目前为止,读者所看到的只是内部类的典型用途。通常,如要所读、写的代码包含了内部类,那么它们都是“平凡的”内部类,简单并且容易理解。然而,内部类的语法覆盖了大量其他的更加难以理解的技术。例如,可以在一个方法里面或者在任意的作用域内定义内部类。这么做有两个理由:
- 如前所示,你实现了某类型的接口,于是可以创建并返回对其的引用。
- 你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
在后面的例子中,先前的代码将被修改,以用来实现:
- 一个定义在方法中的类
- 一个定义在作用域内的类,此作用域在方法的内部
- 一个实现了接口的匿名类
- 一个匿名类,它扩展了有非默认构造器的类
- 一个匿名类,它执行字段初始化
- 一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)
第一个例子展示了在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类。这被称作局部内部类:
public class Parcel5 { public Destination destination(String s){ 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"); }}
PDestination类是destination()方法的一部分,而不是Parcel5的一部分。所以,在destination()之外不能访问PDestination。注意出现在return语句中的向上转型--返回的是Destination的引用,它是PDestination的基类。当然,在destination()中定义了内部类PDestination,并不意味着一旦dest()方法执行完毕,PDestination就不可用了。
你可以在同一个子目录下的任意类中对某个内部类使用类标识符PDestination,这并不会有命名冲突。
下面的例子展示了如何在任意的作用域嵌入一个内部类
public class Parcel6 { private void internalTracking(boolean b) { if (b) { class TrackingSlip { private String id; public TrackingSlip(String id) { this.id = id; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("Slip"); String slip = ts.getSlip(); }// TrackingSlip ts = new TrackingSlip(); } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); }}
TrackingSlip类被嵌入在if语句的作用域内,这并不是说该类的创建是有条件的,它其实与别的类一起编译过了。然而,在定义TrackingSlip的作用域之外,它是不可用的;除此这外,它与普通类一样。
匿名内部类
下面的例子看起来有点奇怪:
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的引用。上述匿名内部类的语法是下述形式的简化形式:
public class Parcel7b { 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) { Parcel7b p = new Parcel7b(); Contents c = p.contents(); }}
在这个匿名内部类中,使用了默认的构造器来生成Contents。下面的代码展示的是,如果你的基类需要一个有参数的构造器,应该怎么办:
class Wrapping{ private int i; public Wrapping(int x){ i = x; } public int value(){ return i; }}public class Parcel8 { public Wrapping wrapping(int x) { return new Wrapping(x){ public int value(){ return super.value(); } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); int value = w.value(); System.out.println(value); }}
只需简单地传递合适的参数给基类的构造器即可,这里是将x传进new Wrapping(x)。尽管Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当作公共“接口”来使用
在匿名内部类末尾的分号,并不是用来标记此内部类结构的。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类罢了。因此,这与别的地方使用的分号是一致的。
在匿名类中定义字段时,还能够对其执行初始化操作
public class Parcel9 { 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) { Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); }}
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的,就像你在destination()的参数中看到的那样。如果你忘记了,将会得到一个编译时错误消息。
如果只是简单地给一个字段赋值,那么此例中的方法是很好的。但是,如果想做一些类似的构造器的行为,该怎么办呢?在匿名类中不可能有命名构造器(因为它根本没名字!),但是通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果,就像这样
abstract class Base{ public Base(int i) { System.out.println("Base constructor, i = " + i); } public abstract void f();}public class AnonymousConstructor { public static Base getBase(int i) { return new Base(i) { @Override public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(48); base.f(); }}
在此例中,不要求变量i一定是final的。因为i被传递给匿名类的基类的构造器,它并不会在匿名类内部被直接使用。
下例是带实例初始化的“parcel”形式。注意destination()的参数必须是final的,因为它们是在匿名类内部使用的。
public class Parcel10 { public Destination destination(final String dest, final float price) { return new Destination() { private int cost; { cost = Math.round(price); if (cost > 0) { 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); }}
在实例初始化操作的内部,可以看到有一段代码,它们不能作为字段初始化动作的一部分来执行(就是if语句)。所以对于匿名而言,实例初始化的实际效果就是构造器。当然它受到了限制--你不能重载实例初始化方法,所以你仅有一个这样的构造器。
匿名内部类与正规的继承相比有些限制,因为匿名内部类即可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。
参考文献
《Java编程思想》10.5方法和作用域内的内部类
《Java编程思想》10.6匿名内部类
- java炒冷饭系列11 方法和作用域内的内部类 与 匿名内部类
- 在方法和作用域内的内部类
- 10.5 在方法和作用域内的内部类
- 内部类在方法和作用域内的用途
- 在方法和作用域内的内部类
- java炒冷饭系列14 内部类标识符
- java内部类与匿名内部类作用是什么?
- java炒冷饭系列13 内部类的继承
- java的内隐类之匿名内部类的成员匿名内部类和方法匿名内部类
- Java内部类与匿名内部类
- JAVA内部类 与 匿名内部类
- Java 内部类与匿名内部类
- Java 内部类与匿名内部类
- Java内部类与匿名内部类
- Java -> 内部类与匿名内部类
- java 内部类 和 匿名内部类
- java 内部类和匿名内部类
- java内部类和匿名内部类
- LeetCode 125:Valid Palindrome(c++)
- 2017技术书单
- 【LeetCode】5. Longest Palindromic Substring
- 实现"输入框"获得焦点时外边框变蓝
- 【面试题】剑指offer12--打印1到最大的n位数
- java炒冷饭系列11 方法和作用域内的内部类 与 匿名内部类
- 饭卡
- java JSON操作所要用到的jar包
- FtpUtil
- 一个人的朝圣
- jsp中的include标签引用页面需注意路径问题
- hiberbate学习之一对多
- Eloquent JavaScript 笔记 十六:Drawing on Canvas
- HttpClientUtil