lambda表达式
Java 8 加入的新特性,lambda表达式是一个可传递的代码块,可以在以后执行一次或多次.在接口只有一个方法时,以表达式的形式简写接口的实列代码.说起来有点抽象,可以理解为让代码少写几行.
格式
参数,箭头(->)以及一个表达式
-> : lambda 操作符
左边: 接口方法的形参
右边: 方法体
使用
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口(functional interface)。
常见函数式接口
之前已有的函数式接口:
- java.lang.Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
- java.util.Comparator
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
java 8 新增函数式接口:
可以自己去java.util.function下查看
- java.util.function.Consumer
用来消费某个实例
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
- java.util.function.Supplier
用来创建某个实例
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
- java.util.function.Predicate
进行断言操作
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
- java.util.function.Predicate
函数的运用
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
示例
通过示例代码展示lambda表达式的使用
1.Consumer接口
@Test
public void consumerTest() {
//需求,打印传入的String字符串
//1.传统方式的使用,使用内部类的方式
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String s) {
//执行逻辑...
System.out.println(s);
}
};
//调用
consumer1.accept("传统方式");
//2.使用完整lambda方式
//只需要方法的参数和方法体,通过-> 符号连接
Consumer<String> consumer2 = (String s) -> {
//执行逻辑...
System.out.println(s);
};
//调用
consumer2.accept("完整lambda方式");
//3.参数部分简写, Consumer<String> 部分已经定义了参数类型,所以形参的类型可以去掉
//当只有一个参数时,,可以去掉括号
Consumer<String> consumer3 = s -> {
//执行逻辑...
System.out.println(s);
};
//调用
consumer3.accept("简写参数");
//4.简写大括号
//当方法体中只有一行代码时,可以去掉大括号
Consumer<String> consumer4 = s -> System.out.println(s);
//调用
consumer4.accept("最终写法");
}
1.Supplier接口
@Test
public void supplierTest() {
//需求:获取最大的Integer值
//1.传统方式的使用,使用内部类的方式
Supplier<Integer> supplier1 = new Supplier() {
@Override
public Object get() {
//执行逻辑...
return Integer.MAX_VALUE;
}
};
//调用
Integer integer1 = supplier1.get();
System.out.println(integer1);
//2.使用完整lambda方式
//等号左边不变
//等号右边,只需要方法的参数和方法体,通过-> 符号连接
//没有参数时,就使用空括号
//不需要返回值,lambda会自己推导
Supplier<Integer> supplier2 =() -> Integer.MAX_VALUE;
Integer integer2 = supplier2.get();
System.out.println(integer2);
}
3.Predicate接口
@Test
public void predicateTest() {
//需求:判断传入数值是否等于0
//1.传统方式的使用,使用内部类的方式
Predicate<Integer> predicate1 = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer == null ? false : integer.equals(0);
}
};
//调用
System.out.println(predicate1.test(null));
//2.使用完整lambda方式
//等号左边不变
//等号右边,只需要方法的参数和方法体,通过-> 符号连接
//只有一个参数时,去掉括号
//不需要返回值,lambda会自己推导
Predicate<Integer> predicate2 = integer -> integer == null ? false : integer.equals(0);
System.out.println(predicate2.test(0));
}
4.Function接口
@Test
public void functionTest() {
//需求:传入一段英文字符串,返回大写的字符串
//1.传统方式的使用,使用内部类的方式
Function<String, String> function1 = new Function<String, String>() {
@Override
public String apply(String s) {
return s == null ? null : s.toUpperCase();
}
};
//调用
System.out.println(function1.apply("abc"));
//2.使用完整lambda方式
//等号左边不变
//等号右边,只需要方法的参数和方法体,通过-> 符号连接
//只有一个参数时,去掉括号
//不需要返回值,lambda会自己推导
Function<String, String> function2 = s -> s == null ? null : s.toUpperCase();
System.out.println(function2.apply("lambda"));
}
补充
方法引用
有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。
可以通过下面三种方式进行方法引用,很多人就说了,对象直接掉方法,直接用.就行了,还用::,又有人说了类掉静态方法很好理解,类调静态方法又是怎么回事?
- object::instanceMethod
通过对象::调用方法
- Class::staticMethod
通过类::调用静态方法
- Class::instanceMethod
通过类:: 非静态方法
在前2种情况中,方法引用等价于提供方法参数的lambda表达式。
举列子说明
1.对象::方法
@Test
public void methodTest1() {
//需求:打印传入的String字符串
//lambda写法
Consumer<String> consumer1 = s -> System.out.println(s);
//调用
consumer1.accept("lambda写法");
//分析,其实就是传入了一个字符串,然后调用了System中PrintStream中的println方法
//是不是可以直接抵用呢,对象方法
//PrintStream printStream = System.out;
//参数就是传入值,参数都省略
Consumer<String> consumer2=System.out::println;
consumer2.accept("对象::方法");
}
2.类::静态方法
@Test
public void methodTest2() {
//需求:传入一个对象,将对象转化String类型
//lambda写法
Function<Object, String> function1=o -> String.valueOf(o);
//调用
System.out.println(function1.apply(1l));
//valueOf方法是String中的静态方法,可以直接通过String类来调用
//传入参数为valueOf方法所需参数,省略
Function<Object, String> function2=String::valueOf;
System.out.println(function2.apply(2l));
}
2.类::非静态方法
这种用起来是有条件的,第1个参数会成为方法的目标。例如,String::compareToIgnoreCase等同于(x,y)->x.compareToIgnoreCase(y)。
这里以Comparator接口为例
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
@Test
public void methodTest3() {
//需求:比较两个字符
//lambda写法
Comparator<String> comparator1=(s1,s2)-> s1.compareToIgnoreCase(s2);
//调用
System.out.println(comparator1.compare("a","A"));
//因为使用了String中的compareToIgnoreCase方法,s1.compareToIgnoreCase(s2)可以简写成String::compareToIgnoreCase
Comparator<String> comparator2=String::compareToIgnoreCase;
//调用
System.out.println(comparator2.compare("a","b"));
}
总结
- 除了构造器引用外,lambda表达式的语法已经讲完了.有兴趣的话可以自行了解构造器引用,与方法引用的方式差不多.
- lambda语法不复杂,就是一个熟悉的过程.没有必要非要用lambda表达式.匿名内部类的方式是必须要会的,lambda要能看得懂.
- lambda让代码更简洁,Stream API 中会用到