文章目录
- Lambda表达式
- 1 匿名内部类
- 1) 普通类继承重写方法
- 2) 匿名内部类重写方法
- 3) 匿名内部类中有多个方法
- 2 Lambda表达式
- 函数式接口
- Lambda表达式
- 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修饰的方法也是非抽象方法,有自己的方法体,在接口中定义一个静态方法,该方法可以直接用< 接口名.方法名() >的形式来调用。相当于调用类的静态方法一样。