Java-Lambda表达式

11次阅读

共计 3722 个字符,预计需要花费 10 分钟才能阅读完成。


可以将 Lambda 表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

  • 匿名 – 因为它不像普通的方法一样有一个明确的名称。
  • 函数 – 说它是函数是因为 Lambda 函数不像方法那样属于某个特定的类,但和方法要一样,Lambda 有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • 传递 – Lambda 表达式可以作为参数传递给方法或存储在变量中。
  • 简洁 – 无需像匿名类那样写很多模板代码

    使用 Lambda 的最终结果就是你的代码变得更清晰、灵活。打比方,利用 Lambda 表达式,可以更为简洁地自定义一个 Comparator 对象。

举个例子:

java8 之前

Comparator <Apple> byWeight = new Comparator<Apple>(){public int compare(Apple a1,Apple a2){return a1.getWeight().compareTo(a2.getWeight());
    }
};

使用 lambda 表达式

(Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

lambda 表达式由三个部分

列出 lambda 表达式的使用案例

1.2 在哪里可以使用 lambda

1.2.1 函数式接口

函数式接口 就是 只定义一个抽象方法的接口。Lambda 表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。

Predicate

java.util.function.Predicate<T> 接口定义了一个名叫 test 的抽象方法,它接受泛型 T 对象,并返回一个 boolean。在需要表示一个涉及类型 T 的布尔表达式时,就可以使用这个接口。

@FunctionalInterface
public interface Predicate<T> {boolean test(T t);
}

举个例子使用这个接口

public static <T> List<T> filter(List<T> list, Predicate<T> p){List<T> results = new ArrayList<>();
    for (T s: list){if(p.test(s)){results.add(s);
        }    
    }
    return results;
}

Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Consumer

java.util.function.Consumer<T> 定义了一个名叫 accept 的抽象方法,它接受泛型 T 对象,没有返回(void)。

@FunctionalInterface
public interface Comsumer<T> {void accept(T t);
}@FunctionalInterface
public interface Function<T, R> {R accept(T t);
}

用这个接口创建一个方法

public static <T> void forEach(List<T> list, Predicate<T> c){for (T i: list){c.accept(i);
    }
}

forEach(Arrays.asList(1,2,3,4,5),
      (Interger i) -> System.ou.println(i)
    );
Function

java.util.function.Function<T, R> 定义了一个名叫 apply 的抽象方法,它接受一个泛型 T 对象,并返回一个泛型 R 的对象。

@FunctionalInterface
public interface Function<T, R> {R accept(T t);
}
public static <T, R> List<R> map(List<T> list, Function<T, R> f){List<R> result = new ArrayList<>();
    for (T s: list){result.add(f.apply(s));
    }
    return result;
}
List<Integer> l =map(Arrays.asList("lambdas","in","action"), (String s) -> s.length());

1.3 方法引用

方法引用可以重复使用现有的方法定义,并像 Lambda 一样传递它们。

先前

invenstory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

使用方法引用和 comparing

inventory.sort(comparing(Apple::getWeight));

1.3.1 如何构建方法引用

方法引用主要有三类。

  • 指向静态方法的方法引用(Integer::parseInt)
  • 指向任意类型实例方法的方法引用(String::length)
  • 指向 现有对象的实例方法 的方法引用(Transaction::getValue)

下面用图可以让你更清楚地理解

1.3.2 构造函数引用

可以利用现有构造函数的名称和关键字来创建它的一个引用 ClassName:new

例 1:

Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();

等价于

Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();

例 2:如果有一个具有两个参数的构造函数 Apple(String color , Integer weight), 它就适合 BiFunction 接口的签名,可以这样写

BiFunction<String,Integer,Apple> c2 = Apple::new;
Apple c3 = c3.apply("green",110);

例 3:如果有三个参数的构造函数呢?Color(int , int , int),你需要自己创建一个与构造函数引用的签名匹配的函数式接口

1.4 复合 Lambda 表达式的有用方法

1.4.1 比较器复合

Comparator<Apple> c = Comparator.comparing(Apple::getWeight);

1. 逆序

inventory.sort(comparing(Apple::getWeight).reversed());

2. 比较器链

如果两个苹果一样重怎么办,哪个苹果应该排在前面?这时候可能需要再提供一个 Comparator 来进一步比较。
thenComparing 就是做这个用的。它接受一个函数作为参数(与 comparing 方法一样),如果两个对象用第一个 Comparator 比较之后是一样的,就提供第二个 Comparator:

inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));

1.4.2 谓词复合

谓词接口包括三个方法:negate、and 和 or.

  • 使用 negate 方法返回一个 predicate 的非,例如筛选出不是红色的苹果
Predicate<Apple> notRedApple = redApple.negate();
  • and
    可以用 and 方法将两个 Lambda 组合起来:
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
  • or
Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getcolor)));

1.4.3 函数复合

还可以把 Function 接口所代表的 Lambda 表达式复合起来。Function 接口有两个默认方法:andThen 和 compose。它们都会返回 Function 的一个实例。

andThen 方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数。

比如函数 f 给数字加 1,另一个函数给数字乘 2:

Function<Integer,Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
Function<Integer,Integer> h = f.andThen(g);
int result = h.apply(1);

在数学上意味着g(f(x))

compose 方法先把给定的函数用作 compose 的参数里面给的那个函数,然后再把函数本身用于结果。

Function<Integer,Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
Function<Integer,Integer> h = f.compose(g);
int result = h.apply(1);

在数学上意味着f(g(x))

正文完
 0