Lambda表达式

函数式接口

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);
...
}

可以看到实际上Predicate只有一个需要显示实现的方法,所以可以用@FunctionalInterface,它是一个函数式接口。

 

示例

public class Bear {

    private String color;

    public Bear(String color) {
        this.color = color;
    }

    public static List<Bear> filter(List<Bear> bears, Predicate<Bear> predicate) {
        List<Bear> bears1 = new ArrayList<>();
        for (Bear bear : bears) {
            if (predicate.test(bear)) {
                bears1.add(bear);
            }
        }
        return bears1;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    
    public static void main(String[] args) {
        filter(List.of(new Bear("green"), new Bear("red")), (Bear b) -> b.getColor().equals("red"));
    }
}

 

Consumer

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
...
}

Consumer翻译就是消费者了,所以可以理解为这个接口的作用是消费对象,因此可以看到它是void的

 

示例

public class Bear {

    private String color;

    public Bear(String color) {
        this.color = color;
    }

    public static List<Bear> forEach(List<Bear> bears, Consumer<Bear> consumer) {
        List<Bear> bears1 = new ArrayList<>();
        for (Bear bear : bears) {
            consumer.accept(bear);
        }
        return bears1;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public static void main(String[] args) {
        forEach(List.of(new Bear("green"), new Bear("red")), 
                (Bear bear) -> System.out.println(bear.getColor()));
    }
}

 

Function

@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);
...
}

这个接口就是接受一个T类型的对象,映射返回一个R类型的对象。

 

示例

public class Bear {
    public static <T, R> List<R> map(List<T> bears, Function<T, R> function) {
        List<R> list = new ArrayList<>();
        for (T t : bears) {
            list.add(function.apply(t));
        }
        return list;
    }

    public static void main(String[] args) {
        List<Integer> list = map(List.of("aa", "aas", "sssss"), (String s) -> s.length());
        list.stream().forEach((s) -> System.out.println(s.intValue()));
    }
}

 

以上是三个标准的泛型化接口,为什么要设计成引用类型呢,因为泛型不允许和原始类型一块使用。当然这个问题也不是这么简单的解释。

 

 

 

 

 

 

类型推断

list.stream().forEach(s -> System.out.println(s.intValue()));

这行代码并没有定义s的类型,但是它是会进行上下文判断的。

 

使用局部变量

        int num = 22;
        Runnable r = () -> System.out.println(num);       ok
        int num = 22;
        Runnable r = () -> System.out.println(num++);      error
        int num = 22;
        Runnable r = () -> System.out.println(num);        error
        num++;

lambda中是不允许修改其引用值的。

实际上我之前研究过,此处就是单纯的copy by value, not copy by reference.对比其它的语言,这无疑是一大败笔,当然会改善,但是不知道什么时候,希望尽快,它是个灾难。

lambda可以没有限制的捕获实例变量或者静态变量,但是局部变量必须显示的声明为final,或者不声明但是实际上是final,lambda捕获的局部变量必须是最后一次赋值完成的。

 

方法引用

inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); 
inventory.sort(comparing(Apple::getWeight)); 

 

 

 

 方法引用的三类:

指向静态方法的引用:Integer中的parseInt方法,写作Integer::parseInt

指向任意类型实例方法的方法引用:String::length

指向现有对象的实例方法引用:下面分析

 

第二种就是你在引用对象的方法,比如,(String s) -> s.toUpperCase()就可以写成 String::toUpperCase

第三种就是调用外部对象的方法,比如 () -> expenseiveTransaction.getValue() 就可以写成 expensiveTransaction::getValue,  这种也是较为常用的

 

 

 

举个常用的例子,比如你要对List<String>进行排序,可以这样:

        List<String> list = Arrays.asList("a", "v", "c");
        list.sort((s1, s2) -> s1.compareToIgnoreCase(s2));

也可以只方法引用:

        list.sort(String::compareToIgnoreCase);

 

构造函数引用

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

等效于:

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

多参数的构造器

        BiFunction<String, Integer, Apple> c1 = (color, weight) -> new Apple(color, weight);      这里一个细节就是你需要将要创建的对象放在泛型的尾部
        BiFunction<String, Integer, Apple> c2 = Apple::new;

 

一个示例应用,大概就是根据苹果类型和重量新建苹果对象

    public Apple(int weight) {
        this.weight = weight;
    }

    static Map<String, Function<Integer, Apple>> map = new HashMap<>();
    static {
        map.put("apple1", Apple::new);
        map.put("apple2", Apple::new);
    }
    public static Apple giveMeApple(String appleType, Integer weight) {
        return map.get(appleType.toLowerCase()).apply(20);
    }

 

复合Lambda表达式

比较器复合

逆序

        apples.sort(Comparator.comparing(Apple::getWeight).reversed());

比较器链

        apples.sort(Comparator.comparing(Apple::getWeight)
                .reversed()
                .thenComparing(Apple::getColor)    // 如果两个Apple一样重,那么再比较颜色,当然这里用颜色比较是不合适的
        );

 

谓词复合

 

 

函数复合

    public static void main(String[] args) {
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;
        Function<Integer, Integer> h = f.andThen(g);
        System.out.println(h.apply(1));
    }
输出4

andThen实际上是 g(f(x))

    public static void main(String[] args) {
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;
        Function<Integer, Integer> h = f.compose(g);
        System.out.println(h.apply(1));
    }
输出3

compose实际上是f(g(x))

 

 

 这种函数复合有什么用呢?

 

 

 

小结

 

 

 《Java8实战》

 
posted @ 2019-12-30 20:28  zhangyu63  阅读(165)  评论(0编辑  收藏  举报