Java 匿名类详解

来源:互联网 发布:python md5解密算法 编辑:程序博客网 时间:2024/05/21 07:23

匿名内部类深究    

       接着刚才的内部类总结,我们一起来讨论匿名内部类这个小怪兽。

      首先,我们要知道,匿名内部类适合创建那种只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。

       定义匿名内部类的格式如下:

       new 实现接口()||父类构造器(实参列表)

       {

    //匿名内部类的类体部分

};//注意这里有一个分号

        匿名内部类的两条规则:

       1.匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义为抽象类;

       2.匿名内部类不能定义构造器。(注意:其他内部类可用有自己的构造器!!由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可用定义初始化块,可以通过实例初始化块来完成构造器需要完成的事情。

        问:匿名内部类这么复杂,那么它有什么作用呢?

        答:最常用的创建匿名内部类的方式是需要创建某个接口类型的对象。

        说到这里,可能还是不太明白,我们先通过一个例子来使用匿名内部类,再来介绍它的作用:

        

[java] view plain copy
 print?
  1. interface Product{  
  2.     public double getPrice();  
  3.     public String getName();  
  4. }  
  5.   
  6. public class Test1{  
  7.       
  8.     public void test(Product p)  
  9.     {  
  10.         System.out.println("购买了一个"+p.getName()+",花掉了"+p.getPrice());  
  11.     }  
  12.     public static void main(String[] args){  
  13.         Test1 tt = new Test1();  
  14.         //此处调用test方法时,需要传入一个Productt参数  
  15.         //此处传入其匿名实现类的实例  
  16.         tt.test(new Product(){  
  17.             <strong>public double getPrice()  
  18.             {  
  19.                 return 567.8;  
  20.             }  
  21.             public String getName()  
  22.             {  
  23.                 return "AGP显卡";  
  24.             }</strong>  
  25.         });  
  26.     }  
  27. }  
        上面的这个程序中的Test1类定义了一个test()方法,该方法需要一个Product对象来作为参数,但Product只是一个接口,无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入该方法---如果这个Product接口实现类只需要一次使用,则可采用上面程序中的方式,定义一个匿名内部类。

       正如上述中的程序,定义匿名内部类时无须class关键字,而是在定义匿名类时直接生成该类的匿名内部类的对象。上面代码中的粗体部分就是匿名内部类的类体部分。

       由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。

       对于刚才创建Product实现类对象的代码,也可以拆分成如下代码:

       

[java] view plain copy
 print?
  1. class Test1 implements Product{  
  2.     public double getPrice()  
  3.     {  
  4.         return 567.8;  
  5.     }  
  6.     public String getName(){  
  7.         return "AGP显卡";  
  8.     }  
  9. }  
  10. ta.test(new Test1());   
        对比上述代码和前面的匿名内部类的类体,我们可以发现,它们完全一样,但显然,采用匿名内部类的写法更为简洁!!

       当通过实现接口来创建匿名内部类时,匿名内部类也不能显式创建构造器,因此匿名内部类只有一个隐式的构造器,故new 接口名后的括号里不能传入参数值。

       但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参列表。

       

[java] view plain copy
 print?
  1. abstract class Device  
  2. {  
  3.     private String name;  
  4.     public abstract double getPrice();  
  5.     public Device(){}  
  6.     public Device(String name)  
  7.     {  
  8.         this.name=name;  
  9.     }  
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16. }  
  17. public class Test1{  
  18.       
  19.     public void test(Device d)  
  20.     {  
  21.         System.out.println("购买了一个"+d.getName()+",花掉了"+d.getPrice());  
  22.     }  
  23.     public static void main(String[] args){  
  24.         Test1 tt = new Test1();  
  25.         //调用有参数的构造器创建Device匿名实现类的对象  
  26.         tt.test(<strong>new Device("电子示波器"){  
  27.             public double getPrice()  
  28.             {  
  29.                 return 67.8;  
  30.             }  
  31.         }</strong>);  
  32.         //调用无参数的构造器创建Device匿名实现类的对象  
  33.         Device d = new Device()  
  34.         {  
  35.             //初始化块  
  36.             {  
  37.                 System.out.println("匿名内部类的初始化块...");  
  38.             }  
  39.             //实现抽象方法  
  40.             <strong>public double getPrice()  
  41.             {  
  42.                 return 56.2;  
  43.             }  
  44.             //重写父类的实例方法  
  45.             public String getName()  
  46.             {  
  47.                 return "键盘";  
  48.             }  
  49.         };</strong>  
  50.         tt.test(d);  
  51.     }  
  52. }  
上述程序中创建了一个抽象父类Device类,这个抽象父类里包含了两个构造器:一个无参数的和一个有参数的。当创建以Device为父类的匿名内部类时,既可以传入参数(第一块粗体部分),也可以不传入参数(第二块粗体部分),代表调用父类的无参数的构造器。

      当创建匿名内部类时,必须实现接口或者抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法,如上面程序中的第二段粗体代码部分,匿名内部类重写了抽象父类Device类的getName()方法,其中getName()方法并不是抽象方法。。

      如果局部变量被匿名内部类访问,则该局部变量相当于自动使用了final修饰。

      

[java] view plain copy
 print?
  1. interface A  
  2. {  
  3.     void test();  
  4. }  
  5. public class ATest {  
  6.       
  7.     public static void main(String[] args) {  
  8.         final int age = 8;  
  9.         A a = new A()  
  10.         {  
  11.             public void test()  
  12.             {  
  13.                 System.out.println(age);  
  14.             }  
  15.         };  
  16.         a.test();  
  17.     }  
  18. }  
注:由于楼主的jdk版本还是8以前的,所以仍然需要使用final修饰哈!

访问权限

那么匿名内部类能访问哪些东西呢?按照规则,可以访问如下内容:

  1. 访问外层Class里面的字段。
  2. 不能访问外层方法中的本地变量。除非变量是final。
  3. 如果内部类的名称和外面能访问的名称相同,则会把名称覆盖掉。
public class A {      private int foo;      public void test() {          Runnable r = new Runnable() {              System.out.println(foo);          };      }  }

匿名类里面不可以有的东西:
1.不能定义静态初始化代码块(Static Initializer)。比如下面的代码是不符合语法的:

public class A {      public void test() {          Runnable r = new Runnable() {              static { System.out.println("hello"); }          };      }  }

2.不能在匿名类里面定义接口。

比如:

public class A {      public void test() {          Runnable r = new Runnable() {              public interface Hello { };          };      }  }

和上面一样,也是为了语义的清晰。interface只能定义静态的。

3.不能在匿名类中定义构造函数。

public class A {      public void test() {          Runnable r = new Runnable() {              public Runnable() { }          };      }  }

因为匿名类没有名字,而构造函数需要把类名作为方法名才能看成构造函数。
匿名类中可以包含的东西有:

  1. 字段
  2. 方法
  3. 实例初始化代码
  4. 本地类

为什么不能定义静态初始化代码

事实上,内部类中不能定义任何静态的东西。

关键字:Inner class cannot have static declarations

参考资料:http://stackoverflow.com/questions/975134/why-cant-we-have-static-method-in-a-non-static-inner-class

StackOverFlow上看起来有一种解释如下。

首先来看一个内部类。

public class A {    public class B {    }  }

它编译之后,会变成下面这种含义:

public class A {    public static class B {      private final A parent;      public B(A parent) {        this.parent = parent;      }    }  }

所以,按照这么说,内部类就是一种语法糖。当我们定义静态变量时,就会产生下面这种歧义。下面的代码看起来没什么问题。

public class A {    private int a;    public class B {      public static void test() {        a = 1;      }    }  }

但是编译之后,问题就来了。

public class A {    private int a;    public static class B {      private final A parent;      public B (A parent) { this.parent = parent; }      public static void test() {        parent.a = 1; // 这里有语法错误      }    }  }

所以,归根结底,Java为了保持清晰的语法,不允许这种有歧义的语法存在。


0 0