요약: 람다 표현식은 Java8이 제공하는 몇 가지 중요한 새 기능 중 하나입니다. 람다 표현식을 빌리면 Java 프로그램 디자인을 더욱 간결하게 만들 수 있습니다. 이 기사는 Java 8의 첫 번째 새로운 기능이며 동작 매개변수화, 람다 표현식 및 메소드 참조에 대해 설명합니다.
람다 표현식은 java8이 제공하는 몇 가지 중요한 새 기능 중 하나입니다. 람다 표현식을 빌리면 Java 프로그램 디자인을 더욱 간결하게 만들 수 있습니다. 최근 새로운 프로젝트는 버전 1.6을 포기하고 Java8을 기반으로 완전히 개발되었습니다. 이 기사는 Java8의 새로운 기능에 대한 첫 번째 기사이며 동작 매개변수화, 람다 표현식 및 메소드 참조에 대해 논의합니다.
동작 매개변수화는 단순히 함수의 본체에 템플릿 클래스 일반 코드만 포함되고 일부는 비즈니스 시나리오에 따라 변경된다는 의미입니다. 매개변수 형태로 함수에 전달됩니다. 동작 매개변수화를 사용하면 프로그램이 잦은 변경에 더 잘 대처할 수 있습니다.
프로그램을 통해 사과를 필터링해야 한다고 가정해 보겠습니다. 먼저 Apple 엔터티를 정의합니다.
/** * 苹果实体 * * @author zhenchao.wang 2016-09-17 12:49 * @version 1.0.0 */ public class Apple { /** 编号 */ private long id; /** 颜色 */ private Color color; /** 重量 */ private float weight; /** 产地 */ private String origin; public Apple() { } public Apple(long id, Color color, float weight, String origin) { this.id = id; this.color = color; this.weight = weight; this.origin = origin; } // 省略getter和setter }
사용자의 초기 요구 사항은 간단할 수 있습니다. 프로그램을 통해 녹색 사과를 걸러낼 수 있으므로 프로그램을 통해 빠르게 달성할 수 있습니다.
/** * 筛选绿苹果 * * @param apples * @return */ public static List<Apple> filterGreenApples(List<Apple> apples) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (Color.GREEN.equals(apple.getColor())) { filterApples.add(apple); } } return filterApples; }
잠시 후 사용자가 새로운 요구 사항을 제시하면 프로그램을 사용하여 빨간 사과를 필터링할 수 있으므로 특별히 빨간 사과를 필터링하는 기능을 추가했습니다.
/** * 筛选红苹果 * * @param apples * @return */ public static List<Apple> filterRedApples(List<Apple> apples) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (Color.RED.equals(apple.getColor())) { filterApples.add(apple); } } return filterApples; }
더 나은 구현은 색상을 매개변수로 함수에 전달하는 것입니다. , 향후 사용자의 다양한 색상 필터링 요청을 처리할 수 있도록:
/** * 自定义筛选颜色 * * @param apples * @param color * @return */ public static List<Apple> filterApplesByColor(List<Apple> apples, Color color) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (color.equals(apple.getColor())) { filterApples.add(apple); } } return filterApples; }
이렇게 디자인하면 더 이상 사용자의 색상 필터링 요구 사항 변경에 대해 걱정할 필요가 없습니다. 그런데 안타깝게도 어느 날 사용자가 무게가 특정 기준에 도달하는 사과를 선택할 수 있어야 한다는 요구사항을 요청했습니다. 이전 강의에서는 무게 기준도 선별 기능에 매개변수로 전달하여 다음과 같은 결과를 얻었습니다. >
/** * 筛选指定颜色,且重要符合要求 * * @param apples * @param color * @param weight * @return */ public static List<Apple> filterApplesByColorAndWeight(List<Apple> apples, Color color, float weight) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (color.equals(apple.getColor()) && apple.getWeight() >= weight) { filterApples.add(apple); } } return filterApples; }이것이 정말 매개변수를 전달하는 좋은 방법인가요? 필터링 조건이 점점 더 많아지고 조합 모드가 더욱 복잡해지면 모든 상황을 고려하고 각 상황에 맞는 전략을 세워야 할까요? 그리고 이러한 기능은 필터링 조건의 일부에서만 다를 뿐입니다. 동일한 템플릿 코드(컬렉션을 순회), 이때
동작을 매개변수화하여 함수가 템플릿 코드만 유지하고 필터링 조건을 추출하여 매개변수로 전달하도록 할 수 있습니다. 우리는 필터 인터페이스를 정의하여 이를 구현했습니다:
/** * 苹果过滤接口 * * @author zhenchao.wang 2016-09-17 14:21 * @version 1.0.0 */ @FunctionalInterface public interface AppleFilter { /** * 筛选条件抽象 * * @param apple * @return */ boolean accept(Apple apple); } /** * 将筛选条件封装成接口 * * @param apples * @param filter * @return */ public static List<Apple> filterApplesByAppleFilter(List<Apple> apples, AppleFilter filter) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (filter.accept(apple)) { filterApples.add(apple); } } return filterApples; }위 동작을 추상화한 후 특정 호출 위치에서 필터링 조건을 설정하고 메서드에서 조건을 매개 변수로 전달할 수 있습니다. 🎜>
public static void main(String[] args) { List<Apple> apples = new ArrayList<>(); // 筛选苹果 List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() { @Override public boolean accept(Apple apple) { // 筛选重量大于100g的红苹果 return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100; } }); }
java.util.Comparator
java.util.concurrent.Callable등 이러한 유형의 인터페이스를 사용할 때 익명 클래스를 사용하여 특정 호출 위치에서 함수의 특정 실행 논리를 지정할 수 있습니다. 그러나 위의 코드 블록으로 판단하면 매우 고객적이지만 간결하지는 않습니다. 충분히, java8에서는 람다를 통해 단순화할 수 있습니다:
// 筛选苹果 List<Apple> filterApples = filterApplesByAppleFilter(apples, (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
2. 람다 표현식 정의
형식 1: 매개변수 목록-> 표현식형식 2: 매개변수 목록-> {표현식 세트}
람다 표현식은 return 키워드를 암시한다는 점에 유의하세요. 이므로 단일 표현식에서는 return 키워드를 명시적으로 작성할 필요가 없지만 표현식이 명령문 모음인 경우 명시적으로 return을 추가하고 중괄호
{ }
//返回给定字符串的长度,隐含return语句 (String s) -> s.length() // 始终返回42的无参方法 () -> 42 // 包含多行表达式,则用花括号括起来 (int x, int y) -> { int z = x * y; return x + z; }
사용자 정의 기능 인터페이스
/** * 苹果过滤接口 * * @author zhenchao.wang 2016-09-17 14:21 * @version 1.0.0 */ @FunctionalInterface public interface AppleFilter { /** * 筛选条件抽象 * * @param apple * @return */ boolean accept(Apple apple); }
AppleFilter
accept(Apple apple)만 포함됩니다. 이를 정의할 때 이 인터페이스에
를 추가했습니다.
@FunctionalInterface
注解,用于标记该接口是函数式接口,不过这个接口是可选的,当添加了该接口之后,编译器就限制了该接口只允许有一个抽象方法,否则报错,所以推荐为函数式接口添加该注解。
jdk为lambda表达式已经内置了丰富的函数式接口,如下表所示(仅列出部分):
函数式接口 | 函数描述符 | 原始类型特化 |
---|---|---|
Predicate8742468051c85b06f0a0af9e3e506b5c | T -> boolean | IntPredicate, LongPredicate, DoublePredicate |
Consumer8742468051c85b06f0a0af9e3e506b5c | T -> void | IntConsumer, LongConsumer, DoubleConsumer |
Funcation469df1b70b2914a3841e0404ec3e3d5d | T -> R | IntFuncation0f1763a9dfcc95d54eac98034f0cdcdd, IntToDoubleFunction, IntToLongFunction0f1763a9dfcc95d54eac98034f0cdcdd, LongFuncation… |
Supplier8742468051c85b06f0a0af9e3e506b5c | () -> T | BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier |
UnaryOperator8742468051c85b06f0a0af9e3e506b5c | T -> T | IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator8742468051c85b06f0a0af9e3e506b5c | (T, T) -> T | IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator |
BiPredicateedf640cc243cef8451ed278a13e0f190 | (L, R) -> boolean | |
BiConsumerabbd655bd3f9f929be0207abcc18a2ef | (T, U) -> void | |
BiFunction96b8c9ba1c8b330d622a8468a6113c2e | (T, U) -> R |
下面分别就
Predicate<T>
、
Consumer<T>
、
Function<T, R>
的使用示例说明。
Predicate8742468051c85b06f0a0af9e3e506b5c
@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); }
Predicate的功能类似于上面的
AppleFilter
,利用我们在外部设定的条件对于传入的参数进行校验,并返回验证结果
boolean
,下面利用
Predicate
对List集合的元素进行过滤:
/** * 按照指定的条件对集合元素进行过滤 * * @param list * @param predicate * @param* @return */ public List filter(List list, Predicate<T> predicate) { List newList = new ArrayList (); for (final T t : list) { if (predicate.test(t)) { newList.add(t); } } return newList; }
利用上面的函数式接口过滤字符串集合中的空字符串:
demo.filter(list, (String str) -> null != str && !str.isEmpty()); Consumer<T> @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); }
Consumer提供了一个accept抽象函数,该函数接收参数,但不返回值,下面利用
Consumer
遍历集合:
/** * 遍历集合,执行自定义行为 * * @param list * @param consumer * @param*/ public void filter(List list, Consumer<T> consumer) { for (final T t : list) { consumer.accept(t); } }
利用上面的函数式接口,遍历字符串集合,并打印非空字符串:
demo.filter(list, (String str) -> { if (StringUtils.isNotBlank(str)) { System.out.println(str); } });
Function469df1b70b2914a3841e0404ec3e3d5d
@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); }
Funcation执行转换操作,输入是类型T的数据,返回R类型的数据,下面利用
Function
对集合进行转换:
/** * 遍历集合,执行自定义转换操作 * * @param list * @param function * @param* @param * @return */ public List filter(List list, Function<T, R> function) { List newList = new ArrayList (); for (final T t : list) { newList.add(function.apply(t)); } return newList; }
下面利用上面的函数式接口,将一个封装字符串(整型数字的字符串表示)的接口,转换成整型集合:
demo.filter(list, (String str) -> Integer.parseInt(str));
上面这些函数式接口还提供了一些逻辑操作的默认实现,留到后面介绍java8接口的默认方法时再讲吧~
类型推断
在编码过程中,有时候可能会疑惑我们的调用代码会去具体匹配哪个函数式接口,实际上编译器会根据参数、返回类型、异常类型(如果存在)等做正确的判定。
在具体调用时,在一些时候可以省略参数的类型,从而进一步简化代码:
/
/ 筛选苹果 List<Apple> filterApples = filterApplesByAppleFilter(apples, (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100); // 某些情况下我们甚至可以省略参数类型,编译器会根据上下文正确判断 List<Apple> filterApples = filterApplesByAppleFilter(apples, apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
局部变量
上面所有例子我们的lambda表达式都是使用其主体参数,我们也可以在lambda中使用局部变量,如下:
int weight = 100; List<Apple> filterApples = filterApplesByAppleFilter(apples, apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);
该例子中我们在lambda中使用了局部变量weight,不过在lambda中使用局部变量必须要求该变量 显式声明为final或事实上的final ,这主要是因为局部变量存储在栈上,lambda表达式则在另一个线程中运行,当该线程视图访问该局部变量的时候,该变量存在被更改或回收的可能性,所以用final修饰之后就不会存在线程安全的问题。
采用方法引用可以更近一步的简化代码,有时候这种简化让代码看上去更加的直观,先看一个例子:
/* ... 省略apples的初始化操作 */ // 采用lambda表达式 apples.sort((Apple a, Apple b) -> Float.compare(a.getWeight(), b.getWeight())); // 采用方法引用 apples.sort(Comparator.comparing(Apple::getWeight));
方法引用通过
::
将方法隶属和方法自身连接起来,主要分为三类:
静态方法
(args) -> ClassName.staticMethod(args)
转换成
ClassName::staticMethod
参数的实例方法
(args) -> args.instanceMethod()
转换成
ClassName::instanceMethod // ClassName是args的类型
外部的实例方法
(args) -> ext.instanceMethod(args)
转换成
ext::instanceMethod(args)
以上就是Java8 新特性之 Lambda 表达式 的内容,更多相关内容请关注PHP中文网(www.php.cn)!