Java8特性一


接口的default(虚拟扩展方法)

interface Calculate {
    double cal(int a);

    // 通过使用 default 关键字向接口添加非抽象方法实现。
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

public class Demo {
    public static void main(String[] args) {
        // 匿名内部类方式
        Calculate calculate = new Calculate() {
            @Override
            public double cal(int a) {
                return sqrt(a*100);
            }
        };

        System.out.println(calculate.cal(100));  // 100.0
        System.out.println(calculate.sqrt(100)); // 10.0
    }
}

Lambda

public class Demo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("lei","ya","di");
        names.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println(names); // [di, lei, ya]
    }
}

在IDEA中,会自动提示用lambda替换:

image-20210922185715344

public class Demo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("lei","ya","di");
        names.sort((o1, o2) -> o1.compareTo(o2));
        System.out.println(names); // [di, lei, ya]
    }
}

仍提示可以替换:

image-20210922185854183

public class Demo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("lei","ya","di");
        names.sort(String::compareTo);
        System.out.println(names); // [di, lei, ya]
    }
}

用 lambda 表达式创建匿名方法,有时候, lambda 表达式仅用于调用现有方法而不做其他事,就可以用方法引用来调用方法

Lambda作用域

image-20210922203238487

image-20210922203232734

对lambda表达式中的实例字段和静态变量都有读写访问权限。无法从 lambda 表达式中访问默认方法。

方法引用

public class Demo {
    public interface Say {
        void run();
    }

    public void invoke(Say say) {
        System.out.println(say.getClass().getName());
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        System.out.println("==== new SayHello() =====");
        demo.invoke(new SayHello()); // 接口实现类
        System.out.println("\n==== lambda =====");
        demo.invoke(()-> System.out.println("Hello")); // lambda
        System.out.println("\n==== new SayHello()::run =====");
        demo.invoke(new SayHello()::run); // 引用方法
        System.out.println("\n==== SayHello::new =====");
        demo.invoke(SayHello::new); // SayHello::new 引用的是构造函数
    }
}

class SayHello implements Demo.Say {
    public SayHello() {
        System.out.println("SayHello 构造方法");
    }
    @Override
    public void run() {
        System.out.println("hello");
    }
}

输出:

==== new SayHello() =====
SayHello 构造方法
java8.SayHello

==== lambda =====
java8.Demo$$Lambda$1/1078694789

==== new SayHello()::run =====
SayHello 构造方法
java8.Demo$$Lambda$2/1149319664

==== SayHello::new =====
java8.Demo$$Lambda$3/999966131

Process finished with exit code 0

后三个都是匿名方法,而且,只有new了才会调用构造函数。

更改invoke函数:

public void invoke(Say say) {
    System.out.println(say.getClass().getName());
    say.run();
}

得到:

==== new SayHello() =====
SayHello 构造方法
java8.SayHello
hello

==== lambda =====
java8.Demo$$Lambda$1/1078694789
Hello

==== new SayHello()::run =====
SayHello 构造方法
java8.Demo$$Lambda$2/1149319664
hello

==== SayHello::new =====
java8.Demo$$Lambda$3/999966131
SayHello 构造方法

Process finished with exit code 0

一个不错的例子:对象工厂

class Person {
    String name;
    int age;
    public Person() {

    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

interface PersonFactory<P extends Person> {
    P create(String name,int age);
}

public class Demo {
    public static void main(String[] args) {
        // 方法引用,这里的create就指向了Person的构造函数
        // 具体哪个根据参数来选择
        PersonFactory<Person> personFactory = Person::new;
        Person person = personFactory.create("aa",13);
        System.out.println(person); // Person{name='aa', age=13}
    }
}

函数式接口

仅仅包含一个抽象方法,但是可以有多个非抽象方法(default)。

public class Demo {

    // 不必须,函数式接口,只包含一个抽象方法
    @FunctionalInterface
    public interface Say {
        void run();
    }

    public static void main(String[] args) {
        Say say = () -> System.out.println("hi");
        say.run(); // hi
    }
}

Supplier接口

public class Demo {
    // Supplier接口指定什么泛型,就可以使用get方法,生产什么类型的数据
    public static int getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
    public static void main(String[] args) {
        int[] arr = {1,5555,999,-90,1314};
        int max = getMax(()->{
            int m = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if(arr[i] >m) {
                    m = arr[i];
                }
            }
            return m;
        });
        System.out.println(max); // 5555
    }
}

Consumer接口

这个是给一个参数到 lambda类的函数去处理,可以和andThen连用。

public class Demo {
    public static void print(String s, Consumer<String> consumer1,Consumer<String> consumer2) {
        consumer1.andThen(consumer2).accept(s);
    }
    public static void main(String[] args) {
        print("di",s -> {
            System.out.println(s+"run");
        },s -> {
            System.out.println(s+"eat");
        });
        // dirun
        //dieat
    }
}

Function接口

public class Demo {
    public static int cal(String s, Function<String,Integer> f1,
                          Function<Integer,Integer> f2) {
        return f1.andThen(f2).apply(s);
    }
    public static void main(String[] args) {
        System.out.println(cal("66", Integer::parseInt,
                integer -> integer*2+1)); // 133
    }
}

Predicate接口

import java.util.function.Predicate;

public class Demo {
    public static void check(String s, Predicate<String> predicate) {
        System.out.println(predicate.test(s));
    }
    public static void main(String[] args) {
        check("abc",s-> s.contains("a")); // true
    }
}

Streams

package java8;


import java.util.*;

public class Demo {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("ddd2");
        stringList.add("aaa2");
        stringList.add("bbb1");
        stringList.add("aaa1");
        stringList.add("bbb3");
        stringList.add("ccc");
        stringList.add("bbb2");
        stringList.add("ddd1");

        stringList
                .stream()
                .filter(s->s.startsWith("a"))
                .forEach(System.out::println);
        // aaa2
        // aaa1

        // 排序是一个 中间操作,返回的是排序好后的 Stream。如果你不指定一个自定义的 Comparator 则会使用默认排序。
        stringList
                .stream()
                .sorted()
                .filter(s->s.startsWith("a"))
                .forEach(System.out::println);
        // aaa1
        // aaa2

        // 中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
        stringList
                .stream()
                .map(String::toUpperCase)
                .sorted(Comparator.reverseOrder())
                .forEach(System.out::println);
        // DDD2
        // DDD1
        // CCC
        // ...

        // Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。
        // 所有的匹配操作都是 最终操作 ,并返回一个 boolean 类型的值。
        System.out.println(
                stringList
                    .stream()
                    .anyMatch(s -> s.startsWith("a"))); // true

        System.out.println(
                stringList
                    .stream()
                    .allMatch(s -> s.startsWith("a"))); //false

        System.out.println(
                stringList
                    .stream()
                    .noneMatch(s -> s.startsWith("a"))); //false

        // count最终操作,返回个数
        System.out.println(
                stringList
                        .stream()
                        .filter(s -> s.startsWith("a"))
                        .count()); //2
        // reduce 这是一个 最终操作 ,允许通过指定的函数来将stream中的多个元素规约为一个元素
        System.out.println(
                stringList
                        .stream()
                        .sorted()
                        .reduce(((s, s2) -> s+"#"+s2)));
        // Optional[aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2]

        int[] ints = {1,4,2,5,6,3,2,10,9};
        Optional<Integer>  res = Arrays.stream(ints).boxed()
                        .reduce(Math::max);
        res.ifPresent(System.out::println); //10
    }
}

parallelStream()并行排序,默认stream是串行的。

Maps

public class Demo {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
            map.putIfAbsent(i, "val" + i); //没有就放,有就返回值
        }
        map.forEach((id, val) -> System.out.println(val));
        map.computeIfPresent(3,(num,val)->val+num); //val33
        System.out.println(map.get(3));
    }
}

Optional

解决空指针异常。就不用加一堆判断了。

具体看这篇文章:理解、学习与使用 JAVA 中的 OPTIONAL

参考

Java 8 新特性总结

Java Lambda 表达式(四):方法引用(Method Reference)

Java学习日志(十四): 函数式接口详解,四种常见函数式接口

理解、学习与使用 JAVA 中的 OPTIONAL