JavaSE基础知识(二十)--Java内部类之匿名内部类(内部类的重点)

Java SE 是什么,包括哪些内容(二十)?

本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!
代码示例:
下面的例子看起来会有点奇怪

// 匿名内部类
   //接口Contents
   interface Contents{
      //方法value()
      int value();
   }
   //外部类Parcel7
   public class Parcel7{
      //外部类方法contents()返回类型为Contents
      public Contents contents(){
         //return语句,返回一个Contents类的对象
         //但是你会发现contents方法将返回值的生成与表示
         //这个返回值的类的定义结合在一起。
         //同时,这个类是匿名的,它没有名字
         //看起来就像:你似乎正要创建一个类Contents的对象,
         //但是在到达语句结束的分号之前,你却插入了一个类的定义
         return new Contents(){
            //int类型的类变量i
            private int i;
            //方法value
            public int value(){
               //返回类变量i的值
               return i;
            }
         //这里要特别提醒一下,需要一个分号
         };
      }
      //程序执行入口main方法
      public static void main(String[] args){  
         //创建外部类Parcel7的对象实例 
         Parcel7 p = new Parcel7();
         //调用方法contents()返回一个Contents类型的对象。
         Contents c = p.contents();
      }
   }

从上面的代码示例中你会发现一个问题:之前不是不能使用接口去创建对象的吗?但是这里却出现了new Contents…代码…匿名内部类是可以直接使用接口的名称去创建对象的,这里并不是直接创建了Contents对象,仅仅是代表了这个匿名内部类是Contents类型的(这种奇怪的语法指的是:创建一个继承自类Contents的匿名类对象,通过new表达式返回的引用被自动向上转型为对Contents的引用)。
上面代码示例中匿名内部类的语法是下述形式的简化:

// 内部类的形式
   //外部类Parcel7b
   public class Parcel7b{
      //内部类实现接口Contents
      class MyContents implements Contents{
         //int类型的类变量i
         private int i;
         //方法value
         public int value(){
            //返回类变量i的值
            return i;
         }
      }
      public Contents contents(){
         return new MyContents();
      }
      //程序执行入口main方法
      public static void main(String[] args){  
         //创建外部类Parcel7b的对象实例 
         Parcel7b p = new Parcel7b();
         //调用方法contents()返回一个Contents类型的对象。
         Contents c = p.contents();
      }
   }

上面的代码示例中的匿名内部类使用了默认的构造方法来生辰Contents,那么,如果你的父类需要一个有参数的构造方法,该如何处理:
代码示例:

// 使用带参数构造方法的匿名内部类
   //父类Wrapping
   public class Wrapping{
      //int类型的类变量i
      private int i;
      //构造方法,带一个int类型的形式参数x
      public Wrapping(int x){
         //为类变量i赋值
         i = x;
      }
      //方法value()
      public int value(){
         //返回类变量i的值
         return i;
      }
   }
   //外部类Parcel8
   public class Parcel8{
      //方法wrapping,带一个int类型的形式参数x,返回Wrapping类型
      public Wrapping wrapping(int x){
         //return语句,直接传入了参数x,表示调用了父类的构造方法
         //这是匿名内部类调用构造方法的方式
         return new Wrapping(x){
            //实现方法value()
            public int value(){
               //返回父类value方法*47的值
               return super.value()*47;
            }
         };
      }
   }

只需简单地传递合适的参数给基类的构造方法即可,这里是将x传进new Wrapping(x),尽管Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当做公共"接口"来使用。
你会注意到,类Wrapping拥有一个要求传递一个参数的构造器,这使得事情变得更加有趣了。
在匿名内部类的末尾的分号,并不是用来标记此内部类的结束的,实际上吗,它标记的是表达式的结束,这不过这个表达式证号包含了匿名内部类而已,因此,这与别的地方使用的分号是一致的。
在匿名内部类中定义字段时,还能够对其执行初始化操作:
代码示例:

// 初始化匿名内部类的字段
   //外部类Parcel9
   public class Parcel9{
      //方法destination(final String dest),带一个final修饰的String
      //类型的形式参数dest,因为dest内部类需要使用,所以必须是final的
      public Destination destination(final String dest){
         //return语句,创建一个匿名内部类
         return new Destination(){
            //为匿名内部类的类变量label赋值,使用的是外部类的dest参数
            private String label = dest;
            //方法readLabel(),返回类型为String类型
            public String readLabel(){
               //返回label的值
               return label;
            }
         }
      }
      //程序执行入口main方法
      public static void main(String[] args){  
         //创建外部类Parcel7b的对象实例 
         Parcel9 p = new Parcel9();
         //调用方法destination(final String dest),获取一个
         //实现了Destination接口类的对象实例。
         Destination d = p.destination("Tamania");
      }
   }

如果定义一个匿名内部类,并且希望它使用一个在其外部类定义的变量或者是对象,那么编译器会要求其参数引用是final的,如果你忘记了,将会得到一个编译时错误消息。
为什么在一个外部类中,如果它的某个变量要被内部类使用到,就需要final修饰?final修饰的作用就是这个变量一旦被赋值,就不能再被改变,这是正确的,因为一旦内部类使用完毕之后,按照Java中代码是顺序执行的机制,在内部类使用了这个变量之后,你再去修改这个变量的值,那么内部类使用的这个值就不再是最新的了。所以需要一致,一旦内部类使用了,后面就不能再修改了,保证前后一致性。
如果你想在一个匿名内部类中执行类似构造方法的初始化,该怎么处理,因为匿名内部类不可能有命名的构造方法(因为这个类根本没名字),只有通过实例初始化,你才达到为匿名内部类创建一个构造方法的效果。
代码示例:

// 为匿名内部类实现一个构造方法初始化
   //抽象类Base
   abstract class Base{
      //构造方法,带一个int类型的形式参数i
      public Base(int i){
         //打印字符串"Base constructor , i=" + i
         System.out.println("Base constructor , i=" + i);
      }
      //抽象方法f()
      public abstract void f();
   }
   //类AnonymousConstructor
   public class AnonymousConstructor{
      //static方法getBase(int i),带一个int类型的形式参数i
      //返回类型为Base类型
      public static Base getBase(int i){
         //return语句,创建一个匿名内部类
         return new Base(i){
            {
               //实例初始化,代表了匿名内部类的构造方法初始化,
               //用一个{ }括起来
               System.out.println("Inside instance initializer");
            }
            //方法f()
            public void f(){
               //打印字符串"In anonumous f()"
               System.out.println("In anonumous f()");
            }
         };
      }
      //程序执行入口main方法
      public static void main(String[] args){
         //调用static方法getBase(int i),返回一个
         //Base类型的匿名内部类对象
         Base base = getBase(43);
         //调用方法f()
         base.f();
      }
   }

在上面的代码示例中,不要求变量i一定是final的,因为i是传递给匿名内部类的父类的构造方法,它并不会在匿名内部类被直接使用。
代码示例二:
下面的示例是带示例初始化的"parcel"形式(注意,方法destination()的参数必须是final的,因为他们是在匿名内部类直接使用的)。

// 实例初始化
   //外部类Parcel10
   public class Parcel10{
      //方法destiantion(final String,final float price)
      //因为两个形式参数都是在内部类里面直接使用的,需要final修饰
      public Destination destination(final String,final float price){
         //return语句,返回一个Destiantion类型,后紧接匿名内部类的创建
         return new Destination(){
            //int类型的类变量cost
            private int cost;
            {
               //实例初始化部门,代表了匿名内部类的构造方法
               cost = Math.round(price);
               //if语句,如果cost的值大于100
               if(cost>100){
                  //则打印字符串"Over budget!"
                  System.out.println("Over budget!");
               }
            }
            //String类型的类变量label,声明的同时给它赋值
            private String label = dest;
            //方法readLabel()
            public String readLabel(){
               //返回label的值
               return label;
            }
         };
      }
      //程序执行入口main方法
      public static void main(String[] args){
         //创建外部类Parcel10的对象实例
         Parcel10 p = new Parcel10();
         //调用方法destiantion(final String,final float price)
         //返回一个Destiantion的类型.
         Destination d = p.destination("Tasmania",101.395F);
      }
   }

在实例初始化的内部,可以看到有一段代码,它们不能作为字段初始化动作的一部分来执行(就是if语句)。所以对于匿名内部类而言,实例初始化的实际效果就是构造方法({ }里面的if语句不能划归到类变量初始化部分,所以只能是构造方法),当然,它受了限制,你不能重载实例初始化方法,所以仅有一个(也就是只能有一个{ })。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值