Java8新特性——Lambda

Lambda表达式

Lambda表达式的作用主要是改进了之前在java中使用的匿名内部类,也就是以前使用匿名内部类来做的事情现在使用Lambda表达式来做。

1 匿名内部类

1) 普通类继承重写方法

通过子类继承父类,重写父类方法来调用子类中的eat()方法。

class Animals{
    public void eat(){
        System.out.println("动物吃东西");
    }
}

class Dog extends Animals{
    @Override
    public void eat(){
        System.out.println("狗吃肉");
    }
}

public class AnonymousInnerDemo {
    public static void main(String[] args) {
        //使用普通类继承来调用方法
        Dog dog = new Dog();
        dog.eat();
    }
}

结果为:
在这里插入图片描述

2) 匿名内部类重写方法

使用匿名内部类的前提条件:

必须存在继承和实现关系的时候才可以使用。因为匿名内部类没有名字,那该如何描述以及new个对象呢?对,要通过继承它的父类或者实现一个接口来达成这一目的。

class Animals{
    public void eat(){
        System.out.println("动物吃东西");
    }
}

class Dog extends Animals{
    @Override
    public void eat(){
        System.out.println("狗吃肉");
    }
}

public class AnonymousInnerDemo {
    public static void main(String[] args) {
          //使用普通类继承来调用方法
          Dog dog = new Dog();
          dog.eat();

        //使用匿名内部类的方式来调用方法,即可以不用写Dog类
        new Animals(){
            @Override
            public void eat(){
                System.out.println("狗吃肉");
            }
        }.eat();
    }
}

结果和使用子类继承的结果相同,可以看出,匿名表达式简化了通过子类继承的方式来调用子类中重写的方法。

3) 匿名内部类中有多个方法

上面的是当父类中有一个方法,匿名内部类只需要重写一个方法并调用;但如果父类中有多个方法,匿名内部类也重写了多个方法,这种情况匿名内部类如何调用方法??

利用多态的思想(匿名内部类的前提是存在继承或实现关系的)

class Animals{
    public void eat(){
        System.out.println("动物吃东西");
    }
    public void sound(){
        System.out.println("动物会说话");
    }
}

public class AnonymousInnerDemo {
    public static void main(String[] args) {
        //使用匿名内部类的方式来调用方法,即可以不用写Dog类
        //使用多态,父类引用指向子类对象
        Animals animals = new Animals(){
            @Override
            public void eat(){
                System.out.println("狗吃肉");
            }
            @Override
            public void sound(){
                System.out.println("汪汪");
            }
        };
        //使用父类引用来调用方法,由于子类重写了方法,因此执行的是子类重写后的方法
        animals.eat();
        animals.sound();
    }
}

结果为:

在这里插入图片描述

2 Lambda表达式

了解完匿名内部类之后,再来看看Lambda表达式,可以对比一下,看Lambda表达式的作用。

函数式接口

只包含一个抽象方法的接口,称为函数式接口。

函数式接口,会在该接口上使用 @FunctionalInterface注解,常见的是Runnable接口。

简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。

所以以前用匿名内部类表示的现在都可以用Lambda表达式来写。

函数式接口扩展知识:上面所定义的函数式接口是正确的,但在有些函数式接口会看到有default修饰的方法,这也是可以的;还会看到该函数式接口中有多个抽象方法,如Comparator接口,里面有compare()和equals()两个抽象方法,但要注意,函数式接口可以被隐式转换为 lambda 表达式,而lambda只能对应一个方法,那么该对应哪个抽象方法呢?答案是compare(),因为函数式接口就是一个有且仅有一个 (除和Object中方法有相同签名) 抽象方法,但是可以有多个非抽象方法(包括default、public static修饰),以及可以有与Object中方法名相同的抽象方法。

Lambda表达式

Lambda 是一个匿名函数,使用它可以写出更简洁、更灵活的代码。

Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

左侧:指定了 Lambda 表达式需要的参数列表

右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。

Lambda表达式语法:

(parameters) -> expression
或
(parameters) ->{ statements; }
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

注意:Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的类型推断。

Lambda表达式例子

表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等。

例1:匿名内部类与Lambda表达式的比较

public class LambdaDemo {

    public static void main(String[] args) {

        //使用匿名内部类
        MathOperation mathOperation = new MathOperation(){
            @Override
            public void outMessage(String mes){
                System.out.println("hello"+mes);
            }
        };
        mathOperation.outMessage("world");

        //使用Lambda表达式
        MathOperation mo = (message) ->
                System.out.println("hello"+message);
        mo.outMessage("world");
    }

    //函数式接口
    interface MathOperation{
        void  outMessage(String mes);
    }
}

注意:虽然Lambda的实现效果和匿名表达式一致,但Lambda针对的是函数式接口,即该接口中只有一个抽象方法;而匿名内部类其实是一个子类,父类中可以有多个抽象方法,因此匿名表达式中可以有多个实现方法。

例2:实现Runnable接口

// 匿名内部类
Runnable r1 = new Runnable(){
    @Override
    public void run(){
        System.out.println("Hello World!");
    }
};

// Lambda表达式
Runnable r2 = () -> System.out.println("Hello World!");

//将实现的Runnable传入Thread中
//方式一:使用匿名内部类
Thread thread1 = new Thread( new Runnable () {
    @Override
    public void run() {
        System.out.println("This is from an anonymous class.");
    }
} );

Thread thread2 = new Thread( () -> {
    System.out.println("This is from an anonymous method (lambda exp).");
} );

例3:一个Lambda表达式有多个目标类型(函数式接口)

如:假设自己写了一个函数式接口,长的跟Runnable一模一样

//自定义的Runnable函数接口
@FunctionalInterface
public interface MyRunnable {
    public void run();
}
//系统中的Runnable函数接口
@FunctionalInterface
public interface Runnable {
    public void run();
}
Runnable r1 = () -> System.out.println("Hello Lambda");
MyRunnable r2 = () -> System.out.println("Hello Lambda");

一个Lambda表达式可以有多个目标类型(函数式接口),只要函数匹配成功即可。

扩展

对函数式接口与接口中方法的认识

函数式接口中的方法:上面所定义的函数式接口是正确的,但在有些函数式接口会看到有default修饰的方法,这也是可以的;还会看到该函数式接口中有多个抽象方法,如Comparator接口,里面有compare()和equals()两个抽象方法,但要注意,函数式接口可以被隐式转换为 lambda 表达式,而lambda只能对应一个方法,那么该对应哪个抽象方法呢?答案是compare(),因为函数式接口就是一个有且仅有一个 (除和Object中方法有相同签名) 抽象方法,但是可以有多个非抽象方法(包括default、public static修饰),以及可以有与Object中方法名相同的抽象方法。

接口中的方法:接口里的变量默认隐含类型是public static final,也就是说是常量。而方法默认类型是public abstract,所以接口的方法都是抽象方法,但事实上,接口中也可以有可实现的方法,如:

  • default方法。

对已有的接口,如果想对接口增加一个新方法,那么需要对实现该接口的所有类进行修改,如果接口实的现类很多,就会带来很大的工作量,而且还很容易破坏以前的代码,带来一些问题。如果把新的方法定义为default方法,就可以避免对其他实现类的修改。

产生的问题:如果接口A和接口B里有一个名字相同并且参数列表也相同的方法都被定义为了default方法,那么当类C实现接口A和接口B的时候就会在编译时报错。若在没有冲突的情况下,当类C成功实现了接口A和接口B以后,类C的实例就可以调用接口A和接口B里的default方法。

  • static方法

static修饰的方法也是非抽象方法,有自己的方法体,在接口中定义一个静态方法,该方法可以直接用< 接口名.方法名() >的形式来调用。相当于调用类的静态方法一样。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值