java匿名内部类和lambda(匿名方法)

匿名内部类

内部类分4种:成员内部类,静态内部类,局部内部类和匿名内部类。我们今天讨论的就是其中最常见的匿名内部类。从名称可以看出,就是没有名字的类,因为没有名字,所以总归和其它普通类有一些些区别:

  1. 类如果没有名字,就只能在定义类的时候直接实例化,否则这个类永远得不到使用,也就没有意义。但这样也就代表这个类不能重复使用,不能在其它地方继续实例化了。
  2. 类如果没有名字,那怎么创建这个对象的引用呢?所以匿名内部类必须继承一个父类或者实现一个接口,然后通过父类引用匿名内部类实例,再通过多态特性调用方法。不过,只能继承一个父类,或实现一个接口。
  3. 定义完就立刻需要实例化,也就代表这个类不可能是抽象类,所以也就不能拥有任何抽象方法。
  4. 类没有名字,所以也不存在类变量,类方法,类初始化块,也没有构造函数。
  5. 匿名内部类如果需要访问外部类局部变量,则只能是final局部变量(java8开始可以不用final修饰,但必须是final使用格式),这是为了保证数据的一致性,外部类局部变量会成为匿名内部类的成员变量。

按上面的限制,我们看demo。

先定义一个输出对象的接口:

public interface IOut {

    void println(String msg);

}

再定义一个抽象类:

public abstract class BaseOut implements IOut {
    private final int bufferSize;

    public BaseOut() {
        bufferSize = 1024;
    }

    public BaseOut(int bufferSize) {
        this.bufferSize = bufferSize;
    }

    @Override
    public abstract void println(String msg);

}

开始演示:

public class Run {

    public static void main(String[] args) {
        // 1.定义的时候直接实例化
        // 2.实现了接口
        final IOut out0 = new IOut() {
            // static String name;  // 4.不能有静态成员
            // static void print() {}   // 4.不能有静态方法
            // static {}    // 4.不能有静态初始化块
            // abstract void print();   // 3.不能有抽象方法
            @Override
            public void println(String msg) {
                System.out.println(msg);
            }
        };
        // 2.继承了父类,无参构造函数
        BaseOut out1 = new BaseOut() {
            @Override
            public void println(String msg) {
                out0.println(msg);  // 5.使用了外部类局部变量,final修饰
            }
        };
        // 2.继承了父类,有参构造函数
        BaseOut out2 = new BaseOut(2048) {
            @Override
            public void println(String msg) {
                out1.println(msg);  // 5.使用了外部类局部变量,java8可以不用final修饰
            }
        };
    }

}

lambda(匿名方法)

在演示lambda之前,先修改下IOut接口,添加默认方法和静态方法:

@FunctionalInterface
public interface IOut {

    void println(String msg);

    /**
     * 默认方法,由实现类调用
     */
    default void print(String msg) {
        System.out.print(msg);
    }

    /**
     * 静态方法
     */
    static void printf(String fmt, Object... args) {
        System.out.printf(fmt, args);
    }

}

最重要的,加了一个注解:@FunctionalInterface,表明这个接口是函数式接口:有且只有一个抽象方法的接口。这个注解是给编译器严格检查使用的,同时也代表:可以添加这个注解的接口,在创建对应的匿名内部类时,可以使用lambda:

public class Run {

    public static void main(String[] args) {
        // 函数式接口,内部类实现只有一个函数,这个时候使用lambda
        IOut out0 = (msg) -> {
            System.out.println(msg);
        };
        // 参数列表只有一个参数,去掉括号
        IOut out1 = msg -> {
            System.out.println(msg);
        };
        // 方法体只有一条语句,去掉花括号和分号
        IOut out2 = msg -> System.out.println(msg);
        // 很巧的,方法体实现只是引用了其它对象的方法,使用方法引用
        IOut out3 = System.out::println;
    }

}

从上面的简单示例可以看出:如果一个接口是函数式接口,并且创建这个函数式接口的匿名内部类时,也只是实现了抽象方法,不添加任何其它方法,保证实现的方法唯一时,就可以使用lambda(匿名函数)。因为确定实现的就是那个函数,所以函数名可以省去,只要申明形参名(为了给方法体使用),形参类型都可以省去,然后使用符号->连接方法体。

lambda的简化写法:

  1. 形参列表如果有且只有一个参数,可以省去括号。
  2. 方法体如果只有一条语句,可以省去花括号和分号。如果有返回值,连return都可以省去。
  3. 方法体如果只有一条语句,又正好满足一定的条件,可以使用更精简的方法引用和构造器引用(本篇不讨论这个)。

lambda能实现的,匿名内部类都能实现,匿名内部类能实现的,lambda有时就无能为力了,主要有以下区别:

  1. 匿名内部类可以为任意接口或类创建匿名内部类,但lambda目标类型只能是函数式接口,只能是接口,就算是只有一个抽象方法的抽象类也不行。
  2. 匿名内部类可以有成员变量,初始化块,多个普通方法,实现或重写多个方法,但lambda只能有一个方法体。
  3. 匿名内部类可以调用父类或接口继承得到的默认方法,lambda不行。

上面说的第3点,具体看demo:

public class Run {

    public static void main(String[] args) {
        // 匿名内部类
        IOut out0 = new IOut() {
            @Override
            public void println(String msg) {
                println(msg);    // 实例方法
                IOut.printf(msg); // 静态方法
            }
        };
        // lambda
        IOut out1 = msg -> {
            // 无法调用继承得到的默认方法
            IOut.printf(msg);
        };
    }

}

 

你可能感兴趣的:(Java)