`

(转)匿名类与内部类

阅读更多
匿名类与内部类
提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看,就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍:

第一次见面
public interface Contents {
int value();
}

public interface Destination {
String readLabel();
}

public class Goods {
private class Content implements Contents {
private int i = 11;
public int value() {
return i;
}
}

protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}

public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}

class TestGoods {
public static void main(String[] args) {
Goods p = new Goods();
Contents c = p.cont();
Destination d = p.dest("Beijing");
}
}

在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样内部类的第一个好处就体现出来了——隐藏你不想让别人知道的操作,也即封装性。
同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:

outerObject=new outerClass(Constructor Parameters);

outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);


注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题——

非静态内部类对象有着指向其外部类对象的引用
对刚才的例子稍作修改:

public class Goods {

private valueRate=2;

private class Content implements Contents {
private int i = 11*valueRate;
public int value() {
return i;
}
}

protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}

public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}


修改的部分用蓝色显示了。在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处——一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象就必须有指向外部类对象的引用。Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。

有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么办?没事,Java里用如下格式表达外部类的引用:

outerClass.this

有了它,我们就不怕这种屏蔽的情况了。

静态内部类
和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来讲还有区别。

除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一层)。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。

局部内部类
是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。

public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new GDestination(s);
}

public static void main(String[] args) {
Goods1 g= new Goods1();
Destination d = g.dest("Beijing");
}
}


上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。

下面有一个更怪的例子: 内部类可以通过程序控制其存在域

public class Goods2{
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) {
Goods2 g= new Goods2();
g.track();
}
}


你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。

匿名内部类
java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的:

new interfacename(){......}; 或 new superclassname(){......};

下面接着前面继续举例子:

public class Goods3 {
public Contents cont(){
return new Contents(){
private int i = 11;
public int value() {
return i;
}
};
}
}

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。

在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:

frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});

有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。
如果你想要初始化它的成员变量,有下面几种方法:
(1)如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final

如果没有final的话,那么内部类使用的外部类局部变量要用哪个值呢?
使用内部类经常是用在Listener这样的。
而Listener中的监听函数被调用时,大部分情况,new内部类所在的函数早就执行完了,此时外部类局部变量早就不存在了。
而加上final后,编译器是这样处理内部类的。如果这个外部局部变量是常量,则在内部类代码中直接用这个常量。如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用。

另外引用别人的一段话:
内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

(2)将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
在这个匿名内部类中使用初始化代码块。

为什么需要内部类?
java内部类有什么好处?为什么需要内部类?

首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。

不过你可能要质疑,更改一下方法的不就行了吗?

的确,以此作为设计内部类的理由,实在没有说服力。

真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题——没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。
分享到:
评论

相关推荐

    java中的匿名内部类总结

    Java 中的匿名内部类是一种特殊的内部类,它没有名字,因此也称为匿名类。匿名内部类是一种简洁的编程方式,能够简化代码编写,但它也有一些限制和特点。 匿名内部类的定义 匿名内部类是指没有名字的内部类,它是...

    内部类知识总结3--匿名内部类

    3. **匿名内部类与接口** 匿名内部类可以用来快速实现一个接口的所有方法,这对于实现简单的回调逻辑非常方便。例如,处理按钮点击事件: ```java button.setOnClickListener(new View.OnClickListener() { @...

    java 匿名内部类的使用规范

    Java匿名内部类是Java语言中一个独特且强大的特性,它允许我们在不需要定义一个单独的类的情况下创建类的实例。这在处理回调、事件监听、单例模式以及其他需要短时间内定义一个简单类的情况时非常有用。本篇文章将...

    MapReduce,泛型,匿名内部类,Runnable和Callable

    总结来说,MapReduce是大数据处理的关键技术,泛型提供了强大的类型安全和代码复用,匿名内部类简化了多线程编程,而`Runnable`和`Callable`则是Java多线程编程的基础。了解和掌握这些概念对于理解现代分布式系统和...

    java基础第七章内部类与异常类.doc

    Java 基础第七章内部类与异常类 Java 语言支持在一个类中定义另一个类,这样的类称做内部类。内部类和外嵌类之间存在着紧密的关系:内部类可以访问外嵌类的成员变量和方法,而外嵌类也可以使用内部类声明的对象作为...

    Java匿名对象与匿名内部类

    Java匿名对象与匿名内部类 Java匿名对象是指没有名字的对象,非匿名对象则是具有名字的对象。在Java中,我们可以使用匿名对象来简化代码,并减少内存的使用。匿名对象的使用场景包括:当对象对方法仅进行一次调用的...

    使用适配器类(匿名内部类).rar_适配器

    适配器模式结合匿名内部类,可以让我们快速地将一个对象转换为期望的接口,以便与已有系统兼容。 描述中的"使用适配器类(匿名内部类)"可能是指创建了一个匿名内部类,该类实现了某个接口或继承了某个类,以便将...

    内部类的使用

    首先,内部类分为四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。成员内部类就像其他成员变量一样,可以直接访问外部类的所有成员,包括私有成员。局部内部类只存在于某个方法内,它的作用范围更小,...

    【IT十八掌徐培成】Java基础第06天-05.匿名内部类-适配器模式.zip

    本讲义将深入探讨Java中的匿名内部类与适配器模式,这两种概念在实际开发中都有着广泛的用途。 首先,让我们了解一下匿名内部类。匿名内部类,顾名思义,就是没有名字的类,它可以在需要使用类的地方直接定义,无需...

    java基础第七章-内部类与异常类.doc

    Java 基础第七章 -内部类与异常类 本章节主要讲解 Java 的内部类和异常类的概念、特点、使用方法和注意事项。 内部类 ------ 内部类是 Java 支持的一种特殊类,定义在另一个类的内部。内部类和外嵌类之间存在关系...

    java和kotlin的内部类静态嵌套类

    内部类分为四种类型:成员内部类、局部内部类、匿名内部类和静态嵌套类。其中,静态嵌套类是与外部类关联但不持有对外部类的引用,因此它可以像普通类一样被静态引用,而无需外部类实例。 Java的静态嵌套类通常用于...

    控制器(内部类)

    内部类分为四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。控制器通常会用到的是成员内部类或静态内部类,因为它们可以访问外部类的属性和方法,同时又具有一定的封装性。 1. **成员内部类**:这种...

    JAVA内部类

    内部类(Inner Classes)是定义在另一个类或接口中的类,它们主要分为四种类型:成员内部类、静态嵌套类、方法内部类和匿名内部类。这些内部类在功能上具有一定的共性,同时又各具特色,适用于不同的场景。 **共性...

    java(Day12)---内部类

    静态内部类与普通成员内部类的区别在于,它可以包含静态成员。它同样可以访问外部类的所有静态成员,但不能直接访问外部类的实例成员。创建静态内部类的对象时,可以直接使用`内部类类名 对象名 = new 外部类类名....

    C#匿名委托和Java匿名局部内部类使用方法示例

    虽然Java的字节码可能看起来与C#的有所不同,但其核心思想是一样的:通过匿名类来实现回调或事件处理。 总结来说,C#的匿名委托和Java的匿名局部内部类都提供了一种简洁的方式来定义一次性使用的函数或方法。它们都...

    内部类介绍

    局部内部类与成员内部类相似,但是它的生命周期随着方法或代码块的执行结束而结束。 - **定义在方法上**: ```java public void method() { class LocalInnerClass { public void doSomething() { System.out...

    Java软件开发实战 Java基础与案例开发详解 8-1 内部类与包装器 共15页.pdf

    ### Java软件开发实战:内部类与包装器 #### 8-1 内部类与包装器 ##### 内部类概述 内部类是Java中一种特殊的类,它被定义在另一个类(称为外部类)中。内部类可以分为四种类型:静态内部类、非静态内部类、局部...

    day05_内部类和Lambda表达式.zip

    3. 静态内部类:与成员内部类类似,但可以像普通静态成员一样被实例化,无需外部类的实例。 4. 匿名内部类:没有名字的内部类,通常用于快速创建一次性使用的类,尤其是在实现接口时。 内部类的主要优点在于它们...

    内部类及lambda表达式的日常练习

    例如,下面的Lambda表达式与上面的匿名内部类等价: ```java button.addActionListener(e -> System.out.println("Button clicked!")); ``` 这里的Lambda表达式`e -> System.out.println("Button clicked!")`接收一...

Global site tag (gtag.js) - Google Analytics