解析java匿名内部类

本文详细介绍了Java 8中匿名内部类的改进,包括其适用范围、创建原理和规则。匿名内部类主要用于方便实现接口,且在Java 8之后,对于被匿名内部类访问的局部变量,不再强制要求使用final修饰,而是采用了'effectivity final'的概念,允许一次性赋值后不可更改。此外,匿名内部类可以继承一个父类或实现一个接口,但不能同时进行,也不能是抽象类,并且拥有一个隐式的无参构造器。

Java 8 改进的匿名内部类

适用范围:

匿名内部类适合于方便程序实现接口,即匿名内部类相当于接口的实现类。

创建原理:

创建匿名内部类在实现接口的时候会立即创建一个没有名字类的实例用来实现某一接口,这个类立即创建立即消失,匿名内部类不能重复使用
(匿名内部类中有一个隐式的无参构造器)

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

new 实现接口 ( ) | 父类构造器 ( 实参列表 )
{
//匿名内部类的实体部分
}

关于匿名内部类还有如下规则:

  1. 从上面定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。(相当于匿名内部类继承了一个父类或者实现了一个接口是接口的实现类)
  2. 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类(抽象类不能实例化)
  3. 匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义初始化块,可以通过实例初始化块来完成构造器需要完成的事情。

最常见的创建匿名内部类的方式是需要创建某个接口类型的对象,如下程序所示:

//定义一个产品的接口
interface Product{
    double getPrice();
    String getName();

}
public class AnonymousTest {
    //定义一个方法实现“买”这种行为;
    public void test (Product p){
        System.out.println("购买了一个"+p.getName()+",花掉了"+p.getPrice());
    };

   public static void main(String[] args) {
        AnonymousTest at=new AnonymousTest();
        //调用test()方法时,需要传递一个参数
        //此处通过匿名内部类实现
        at.test(new Product() {
            @Override
            public double getPrice() {
                return 57.8;
            }

            @Override
            public String getName() {
                return "APG显卡";
            }
        });

    }

    

}

上面程序中的AnonymousTest定义了一个test( ) 方法,该方法需要一个 Product对象作为参数,但Product只是一个接口,无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入该方法(即匿名内部类)

如果这个Prodect接口实现类需要重复使用,则应该将该实现类定义成一个独立类;如果这个Product接口实现类只需使用一次,则可采用上面程序中的方式,定义一个匿名内部类。
//如果需要重复使用Product接口,那么将实现类定义为独立类;

class implementsClass implements Product{

    @Override
    public double getPrice() {
        return 0;
    }

    @Override
    public String getName() {
        return null;
    }
}

定义匿名内部类无须 class 关键字,而是在定义匿名内部类时直接生成该匿名内部类的对象。由于匿名内部类不能是抽象类(抽象类不能实例化),所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法,

下面是对于上面代码的另外一种实现方式(接口需要重复使用,则将实现类定义为一个独立类):

class implementsClass implements Product{

    @Override
    public double getPrice() {
        return 57.8;
    }

    @Override
    public String getName() {
        return "AGP显卡";
    }
    at.text(new implementsClass());

上面两段代码功能完全一样,只不过采用匿名内部类的写法更加简洁。

当通过实现接口来创建匿名内部类时,匿名内部类也不能显式创建构造器,因此**匿名内部类只有一个隐式的无参数构造器,**故 new 接口后的括号里不能穿入参数。但如果通过继承父类来创建匿名内部类时,匿名内部类讲拥有和父类相似的构造器,此处的相似是指拥有相同的形成列表。(当匿名内部类用来实现抽象类的时候可以将抽象类看作是匿名类内部类的父类,即匿名内部类会继承抽象类的所有的方法,也可以使用抽象类中的构造器)

看下面的程序:

//定义一个抽象类
abstract class Device {

    private String name;
    public abstract double getPrice();
    public Device(){};
    public Device(String name){
        this.name=name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class AnonymousInner{
    public void test(Device d){
        System.out.println("购买了一个"+d.getName()+",花掉了"+d.getPrice());
    }

    public static void main(String[] args) {
        AnonymousInner ai=new AnonymousInner();
        //调用有参数的构造器来实现匿名内部类
        ai.test(new Device("电子示波器") {
            @Override
            public double getPrice() {
                return 67.8;
            }
        });
        //调用无参数的的构造器创建匿名内部类
        Device d=new Device() {
            //初始化代码块
            {
                System.out.println("匿名内部类初始化代码块……");
            }
            @Override
            public double getPrice() {
                return 56.3;
            }
            //重写父类的实例方法
            public String getName(){
                return "键盘";
            }
        };
        ai.test(d);
    }
    }

上面程序创建了一个抽象父类Devite 类,这个抽象父类里面包含两个构造器:一个无参数的,一个有参数的。当创建以 Devite为父类的匿名内部类时,既可以传入参数,代表调用父类带参数的构造器;也可以不传入参数,代表调用父类无参数的构造器。当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法。如上面程序中,匿名内部类重写了抽象父类Devite类的 getName ( ) 方法,其中 getName ( ) 方法并不是抽象方法。

在 Java 8 之前,Java 要求被局部内部类、匿名内部类访问的局部变量必须使用 final 修饰,从 Java 8 开始这个限制被取消了,Java 8 更加智能:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了 final 修饰。

interface Student
{
    void getAge();

}

public class InnerDemo {

    public static void main(String [] args)
    {
        int age=10;    //1⃣️
        Student student=new Student() {
            @Override
            public void getAge() {
                //在 Java 8 以前下面语句将提示错误,age 必须使用 final 修饰
                //从 java 8 开始,匿名内部类、局部内部类允许访问非 final 的局部变量
                System.out.println(age);
            }
        };
        student.getAge();
    }
}

Java 8 将这种功能称为“ effectivity final ” ,它的意思是对于被匿名内部类访问的局部变量,可以用 final 修饰,也可以不用 final 修饰,但必须按照油final 修饰的方式来用——也就是一次赋值后,以后不能重新赋值。如上面程序如果在1⃣️代码后添加如下代码:age=20;将会导致编译错误。(即java8以后被匿名内部类访问的局部变量相当于自动加上了final修饰符,不可以对该局部变量就行二次赋值)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值