package com.aaa.entity;
public class Program {
public static void main(String[] args) {
Cal cal = new Cal() {
@Override
public int add(int a, int b) {
return a + b;
}
};
System.out.println(cal.add(1,2));
}
}
interface Cal{
int add(int a,int b);
}
package com.aaa.entity;
public class Program {
public static void main(String[] args) {
Cal cal = (int a,int b)->{
return a + b;
};
System.out.println(cal.add(1,2));
}
}
interface Cal{
int add(int a,int b);
}
如下lambda表达式:
(int a,int b)-> {return a + b;}
如上,本质是一个函数。
一般的函数如下:
int add(int a,int b){
return a + b;
}
有返回值,方法名,参数列表,方法体
lambda表达式只有参数列表和方法体:
(参数列表) -> {
方法体;
}
总共6种情况,接口方法无返回值和有返回值分2种,其中无参数、单个参数和多个参数又分3种情况。
来看示例代码:
public class Test {
public static void main(String[] args) {
I01 i01 = () -> {
System.out.println("无返回值、无参数");
};
I02 i02 = (int a) -> {
System.out.println("无返回值,单个参数。a=" + a);
};
I03 i03 = (int a, int b) -> {
System.out.println("无返回值,多个参数。a=" + a + ",b=" + b);
};
I04 i04 = () -> {
System.out.println("有返回值、无参数");
return 4;
};
I05 i05 = (int a) -> {
System.out.println("有返回值,单个参数。a=" + a);
return 5;
};
I06 i06 = (int a, int b) -> {
System.out.println("有返回值,多个参数。a=" + a + ",b=" + b);
return 6;
};
i01.method();
i02.method(5);
i03.method(5,10);
System.out.println(i04.method());
System.out.println(i05.method(5));
System.out.println(i06.method(5, 10));
}
}
interface I01 {
void method();
}
interface I02 {
void method(int a);
}
interface I03 {
void method(int a, int b);
}
interface I04 {
int method();
}
interface I05 {
int method(int a);
}
interface I06 {
int method(int a, int b);
}
输出:
无返回值、无参数
无返回值,单个参数。a=5
无返回值,多个参数。a=5,b=10
有返回值、无参数
4
有返回值,单个参数。a=5
5
有返回值,多个参数。a=5,b=10
6
Java 8为了使现有的函数更加友好地支持Lambda表达式,引入了函数式接口的概念。
函数式接口本质上是一个仅有一个抽象方法的普通接口,所以又叫SAM接口(Single Abstract Method Interface)。
函数式接口在实际使用过程中很容易出错,比如某人在接口定义中又增加了另一个方法,则该接口不再是函数式接口,此时将该接口转换为Lambda表达式会报错。为了克服函数式接口的脆弱性,并且能够明确声明接口是作为函数式接口的意图,Java 8增加了**@FunctionalInterface**注解来标注函数式接口。
使用@FunctionalInterface注解标注的接口必须是函数式接口,也就是说该接口中只能声明一个抽象方法,如果声明多个抽象方法就会报错。但是默认方法和静态方法不属于抽象方法,因此在函数式接口中也可以定义默认方法和静态方法。
比如这样声明一个函数式接口是被允许的:
@FunctionalInterface
interface InterfaceDemo {
void method(int a);
static void staticMethod() {
...
}
default void defaultMethod() {
...
}
}
@FunctionalInterface注解不是必须的,如果一个接口符合"函数式接口"的定义,那么加不加该注解都没有影响。当然加上该注解能够更好地让编译器进行检查,也能提高代码的可读性。
参数类型可以省略
比如I02 i02 = (int a) -> {System.out.println(...);};
可以写成I02 i02 = (a) -> {System.out.println(...);};
假如只有一个参数,那么()括号可以省略
比如I02 i02 = (a) -> {System.out.println(...);};
可以写成I02 i02 = a -> {System.out.println(...);};
假如方法体只有一条语句,那么语句后的;分号和方法体的{}大括号可以一起省略
比如I02 i02 = a -> {System.out.println(...);};
可以写成I02 i02 = a -> System.out.println(...);
如果方法体中唯一的语句是return返回语句,那么在省略第3种情况的同时,return也必须一起省略
比如I05 i05 = a -> {return 1;};
可以写成I05 i05 = a -> 1;
在Java 8中可以用方法引用来进一步简化Lambda表达式。(虽然两者在底层实现原理上略有不同,但在实际使用中完全可以视为等价)
有时候多个Lambda表达式的实现函数是一样的,我们可以封装成一个通用方法,再通过方法引用来实现接口。
5.1 方法引用语法
如果是实例方法:对象名::实例方法名
如果是静态方法:类名::实例方法名
如果是构造方法:类名::new
来看示例代码:
public class Test {
public void eat(int a) {
System.out.println("吃东西。" + "a=" + a);
}
public static void main(String[] args) {
//Lambda表达式写法:
Dog dog1 = (a) -> System.out.println("吃东西。" + "a=" + a);
Cat cat1 = (a) -> System.out.println("吃东西。" + "a=" + a);
dog1.doSomething(5);
cat1.doSomething(5);
//方法引用写法:
Test test = new Test();
Dog dog2 = test::eat;
Cat cat2 = test::eat;
dog2.doSomething(10);
cat2.doSomething(10);
}
}
@FunctionalInterface
interface Dog {
void doSomething(int a);
}
@FunctionalInterface
interface Cat {
void doSomething(int a);
}
输出结果:
吃东西。a=5
吃东西。a=5
吃东西。a=10
吃东西。a=10
如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现(比如说接口方法与这个构造方法的参数个数、参数类型和返回值都对的上),那么就可以使用构造方法引用。
代码如下:
public class Test {
public void eat(int a) {
System.out.println("吃东西。" + "a=" + a);
}
public static void main(String[] args) {
//Lambda表达式写法:
DogService dogService1 = (name, age) -> new Dog(name, age);
System.out.println(dogService1.getDog("大狗", 5));
//方法引用写法:
DogService dogService2 = Dog::new;
System.out.println(dogService2.getDog("二狗", 3));
}
}
@FunctionalInterface
interface DogService {
Dog getDog(String name, int age);
}
class Dog {
String name;
int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
结果:
Dog{name='大狗', age=5}Dog{name='二狗', age=3}