java8新特性——Lambda表达式和函数式接口

Java Lambda概要

  • Java Lambda表达式是一种匿名函数;它没有声明的方法,即没有访问修饰符、返回值声明和名字。

Java Lambda表达式基本语法

  • Java中的Lambda表达式基本语法:
    • (argument) -> {body}
  • 比如说:
    • (arg1,arg2) -> {body}
    • (type1 arg1,type2 arg2) -> {body}
    • arg1 -> {body}
    • s -> System.out.println(s)

Java Lambda表达式结构

  • 一个Lambda表达式可以有0个或多个参数。
  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如,(int a)和(a)效果相同
  • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a,b)或(int a,int b)或(String a,String b,float c)
  • 空圆括号代表参数集为空。例如:() -> 42
  • 当只有一个参数,且其类型可以推导时,圆括号()可省略。例如:a -> return a * a
  • Lambda表达式的主题可以包含0条或多条语句
  • 如果Lambda表达式的主题只有一条语句,花括号{}可以省略。匿名函数的返回类型与该主体表达式一致
  • 如果Lambda表达式的主体包含一条以上语句,则表达式主体必须包含在花括号{}中(形成代码块),匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

Java Lambda示例

  • Lambda示例说明
    • (int a,int b) -> {return a + b;}
    • () -> System.out.println("hello world");
    • (String s) -> {System.out.println(s);}
    • () -> 42
    • () -> {return 3.1415};

Lambda表达式作用

  • 传递行为,而不仅仅是值
  • 提升抽象层次
  • API重用性更好
  • 更加灵活

方法引用

其实是lambda表达式的一种简化写法。所引用的方法其实是lambda表达式的方法体实现,语法也很简单,左边是容器(可以是类名,实例名),中间是"::",右边是相应的方法名。
本质上可以将方法引用看作是一个“函数指针” ——function pointer。

如下所示:

ObjectReference::methodName
一般方法的引用格式:

  • 1、如果是静态方法,则是ClassName::staticmethodName。如 Object ::equals
  • 2、如果是实例方法,则是Instance::methodName。如Object obj=new Object();obj::equals;
  • 3、如果是实例方法,则可以ClassName::methodName。如Object ::equals;
  • 4、构造函数.则是ClassName::new

方法引用使用场景:lambda表达式只有一行代码,且这行代码调用的这个方法是已经存在的,那么就可以用方法引用去替换掉lambda表达式。

此为方法引用最简单例子:

public class MethodReferenceTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","helloworld");
        list.forEach(item -> System.out.println());

        list.forEach(System.out::println);
    }
}

接下来看一下稍微复杂的例子:

1、如果是静态方法,则是ClassName::staticmethodName。如 Object ::equals

public class Student {

    private String name;

    private int score;

    public Student() {
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public static int compareStudentBySocre(Student student1,Student student2){
        return  student1.getScore() - student2.getScore();
    }

    public static int compareStudentByName(Student student1,Student student2){
        return  student1.getName().compareTo(student2.getName());
    }   
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);

        list.sort((studentParam1,studentParam2) -> Student.compareStudentBySocre(studentParam1,studentParam2));
        list.sort((studentParam1,studentParam2) -> Student.compareStudentByName(studentParam1,studentParam2));
        System.out.println(list);

        list.sort(Student::compareStudentBySocre);
        list.sort(Student::compareStudentByName);
        System.out.println(list);
    }
}

2、如果是实例方法,则是Instance::methodName。如Object obj=new Object();obj::equals;

public class StudentComparator {

    public int compareStudentBySocre(Student student1,Student student2){
        return  student1.getScore() - student2.getScore();
    }

    public int compareStudentByName(Student student1,Student student2){
        return  student1.getName().compareTo(student2.getName());
    }
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);
      
        StudentComparator studentComparator = new StudentComparator();
        list.sort((studentParam1,studentParam2) -> studentComparator.compareStudentBySocre(studentParam1,studentParam2));
        list.sort((studentParam1,studentParam2) -> studentComparator.compareStudentByName(studentParam1,studentParam2));
        list.sort(studentComparator::compareStudentBySocre);
        list.sort(studentComparator::compareStudentByName);
    }   
}

3、如果是实例方法,则可以ClassName::methodName。如Object ::equals;

public class Student {

   ......字段、setter和getter方法省略......

    public int compareBySocre(Student student){
        return  this.getScore() - student.getScore();
    }

    public int compareByName(Student student){
        return  this.getName().compareTo(student.getName());
    }
}

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",70);
        Student student4 = new Student("zhaoliu",60);

        List<Student> list = Arrays.asList(student1,student2,student3,student4);

        list.sort(Student::compareBySocre);
        list.sort(Student::compareByName);
    }
}

4、构造函数.则是ClassName::new

public class MethodReferenceTest2 {

    public static void main(String[] args) {
        List<String> citys = Arrays.asList("shenzhen","shanghai","beijing","chongqing");
        Collections.sort(citys,String::compareTo);

        MethodReferenceTest2 methodReferenceTest2 = new MethodReferenceTest2();
        String test2String = methodReferenceTest2.getString(Student::new);
        String string2 = methodReferenceTest2.getString2("  hello  ", String::new);
    }

    public <T> String getString(Supplier<T> supplier){
        return supplier.get() + "test";
    }

    public <T> T getString2(T str, Function<T,T> function){
        return function.apply(str);
    }
}

可以看出,doSomething方法就是lambda表达式的实现,这样的好处就是,如果你觉得lambda的方法体会很长,影响代码可读性,方法引用就是个解决办法。

函数式接口

函数式接口(functional interface 也叫功能性接口,其实是同一个东西)。简单来说,函数式接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和 java.util.Comparator都是典型的函数式接口。
java 8提供 @FunctionalInterface作为注解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),虚拟机会自动判断, 但 最好在接口上使用注解@FunctionalInterface进行声明,以免团队的其他人员错误地往接口中添加新的方法。

Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现;每个lambda表达式都能隐式的赋值给函数式接口。

关于函数式接口:

  • 1、如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。
  • 2、如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。
  • 3、如果某个接口只有一个抽象方法,但我们并没有给该接口声明@FunctionalInterface注解,那么编译器依旧会将该接口看做是函数式接口。

高阶函数

如果一个函数接收另一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数。

自定义函数式接口

public class Test3 {

    public static void main(String[] args) {
        TheInterface i1 = () -> {};
        System.out.println(i1.getClass().getInterfaces()[0]);

        TheInterface2 i2 = () -> {};
        System.out.println(i2.getClass().getInterfaces()[0]);     
    }
}

@FunctionalInterface
interface TheInterface{
    void myMethod();
}

@FunctionalInterface
interface TheInterface2{
    void myMethod2();
}

我们知道使用Lambda表达式是需要使用函数式接口的,那么,岂不是在我们开发过程中需要定义许多函数式接口,其实不然,java8其实已经为我们定义好了4类内置函数式接口,这4类接口其实已经可以解决我们开发过程中绝大部分的问题,只有一小部分比较特殊得情况需要我们自己去定义函数式接口,下面就简单来学习一下java8内置得4大核心函数式接口。

一、Consumer<T>:消费型接口(void accept(T t))
Consumer接口:该接口定义了一个void accept(T)的抽象方法,其函数描述符为 (T) -> void,如果你需要一个操作某一对象,但无需返回的的函数式接口,那么就可以使用Consumer接口。

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

来看一个简单得例子:

public class Test3 {

    public static void main(String[] args) {
       
        List<String> list = Arrays.asList("hello","world","helloworld");
        list.forEach(item -> System.out.println(item.toUpperCase()));

        List<String> list2 = new ArrayList<>();
        list.forEach(item -> list2.add(item.toUpperCase()));
        list2.forEach(item -> System.out.println(item));

        list.stream().map(item -> item.toUpperCase()).forEach(item -> System.out.println(item));
        list.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}

以上为消费型接口,有参数,无返回值类型的接口。

二、Supplier<T>:供给型接口(T get())
Supplier接口:既然有消费者接口(Consumer),那就要有生产者接口(Supplier),该接口定义了一个 T get() 的抽象方法,其函数描述符为 () -> T,如果你需要一个从某一对象获取到某值的接口,那么就可以用Supplier。

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

来看一个简单得例子:

public class SupplierTest {

    public static void main(String[] args) {
        Supplier<String> supplier = () -> "hello world";
        System.out.println(supplier.get());

        Supplier<Student> supplier1 = () -> new Student("张三",20);
        System.out.println(supplier1.get().getName());

        Supplier<Student> supplier2 = Student::new;
        System.out.println(supplier2.get().getName());
    }
}

public class Student {

    private String name;

    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

上面就是一个供给类型得接口,只有产出,没人输入,就是只有返回值,没有入参

三、Function<T, R>:函数型接口(R apply(T t))
Function接口:该接口定义了一个 R applay(T)类型的抽象函数,它接受一个泛型变量T,并返回一个泛型变量R,如果你需要将一个对象T映射成R,那么就可以使用Function接口。例如我们需要打印一个花名册,但是具体打印的格式需要可灵活配置,那就将花名册打印格式参数化,需要使用 (Student) -> String 类型的函数描述符,与Function接口吻合。

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

下面看一个简单的例子:

public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest test = new FunctionTest();
        int compute1 = test.compute(1, value -> {return 2 * value;});
        System.out.println(compute1);

        int compute2 = test.compute(2, value -> 5 + value);
        System.out.println(compute2);

        int compute3 = test.compute(3, value -> value * value);
        System.out.println(compute3);

        String compute4 = test.convert(5,value -> String.valueOf(value + "helloworld"));
        System.out.println(compute4);

        Function<Integer,Integer> function = value -> value * 2;
        int compute5 = test.compute(4, function);
        System.out.println(compute5);

    }

    public <T> T compute(T a, Function<T,T> function){
        T result = function.apply(a);
        return result;
    }

    public String convert(int a,Function<Integer,String> function){
        String result = function.apply(a);
        return result;
    }
}

下面看一个稍微复杂的例子:

public class FunctionTest2 {

    public static void main(String[] args) {
        FunctionTest2 test = new FunctionTest2();

        int compute = test.compute(2, value -> value * 3, value -> value * value);
        int compute2 = test.compute2(2, value -> value * 3, value -> value * value);
        System.out.println(compute);
        System.out.println(compute2);
    }

    public <T> T compute(T a, Function<T,T> function1,Function<T,T> function2){
        return function1.compose(function2).apply(a);
    }

    public <T> T compute2(T a,Function<T,T> function1,Function<T,T> function2){
        return function1.andThen(function2).apply(a);
    }
}

上面就是一个函数型接口,输入一个类型得参数,输出一个类型得参数,当然两种类型可以一致。

四、Predicate<T>:断言型接口(boolean test(T t))
Predicate接口:该接口定义了一个支持泛型的boolean test( T)的抽象方法,其函数描述符为 (T)-> boolean,现在我们就可以直接使用Predicate接口来替代OToBoolean接口了。

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

下面看一个简单得例子:

public class PredicateTest {

    public static void main(String[] args) {
        Predicate<String> predicate = p -> p.length() > 5;
        boolean test = predicate.test("hello");
        System.out.println(test);
    }
}

下面看一个稍微复杂的例子:

public class PredicateTest2 {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        conditionFilter(list,item -> item % 2 == 0);

        conditionFilter(list,item -> item > 5);

        conditionFilter(list,item -> true);

        //找出集合中所有大于5且为偶数的元素
        conditionFilter2(list,item -> item > 5,item -> item % 2 == 0);

        //找出集合中大于5或为偶数的元素
        conditionFilter3(list,item -> item > 5,item -> item % 2 == 0);

        //找出集合中所有大于5且为偶数的元素基础上取反
        conditionFilter4(list,item -> item > 5,item -> item % 2 == 0);

        boolean test1 = isEqual("hello").test("hello");
        System.out.println(test1);

        boolean test2 = isEqual(new Date()).test(new Date());
        System.out.println(test2);

    }

    public static <T> void conditionFilter(List<T> list, Predicate<T> predicate){
        list.forEach(t -> {
            if(predicate.test(t)){
                System.out.println(t);
            }
        });
    }

    public static <T> void conditionFilter2(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.and(predicate2).test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> void conditionFilter3(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.or(predicate2).test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> void conditionFilter4(List<T> list,Predicate<T> predicate1,Predicate<T> predicate2){
        list.forEach(item -> {
            if(predicate1.and(predicate2).negate().test(item)){
                System.out.println(item);
            }
        });
    }

    public static <T> Predicate<T> isEqual(T t){
        return Predicate.isEqual(t);
    }

}

上面就是一个断言型接口,输入一个参数,输出一个boolean类型得返回值。

五、其他的一些重要函数式接口
**BiFunction<T, U, R> **

参数类型有2个,为T,U,返回值为R,其中方法为R apply(T t, U u)

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

下面看一个简单得例子:

public class FunctionTest2 {

    public static void main(String[] args) {
        FunctionTest2 test = new FunctionTest2();

        int compute3 = test.compute3(2, 3, (value1, value2) -> value1 + value2);
        System.out.println(compute3);

        int compute4 = test.compute4(3, 4, (value1, value2) -> value1 + value2, value -> value * value);
        System.out.println(compute4);
    }

    public <T> T compute3(T a, T b, BiFunction<T,T,T> function){
        return function.apply(a,b);
    }

    public <T> T compute4(T a,T b,BiFunction<T,T,T> biFunction,
                        Function<T,T> function){
        return biFunction.andThen(function).apply(a,b);
    }
}

public class PersonTest {

    public static void main(String[] args) {
        Person person1 = new Person("zhangsan",20);
        Person person2 = new Person("lisi",38);
        Person person3 = new Person("wangwu",40);
        List<Person> list = Arrays.asList(person1,person2,person3);

        List<Person> resultList = getPersonByUsername("zhangsan", list);
        resultList.forEach(person -> System.out.println(person));

        List<Person> personByAge = getPersonByAge(20, list);
        personByAge.forEach(person -> System.out.println(person));

        List<Person> personByAge2 = getPersonByAge2(20, list, (age, personList) ->
                personList.stream().filter(person -> person.getAge() > age).collect(Collectors.toList())
        );
        personByAge2.forEach(person -> System.out.println(person));
    }

    public static List<Person> getPersonByUsername(String username,List<Person> list){
        return list.stream().filter(person -> person.getName().equals(username)).collect(Collectors.toList());
    }

    public static List<Person> getPersonByAge(int age,List<Person> list){
        /*BiFunction<Integer,List<Person>,List<Person>> biFunction = (ageOfPerson,personList) -> {
            return personList.stream().filter(person -> person.getAge() > ageOfPerson).collect(Collectors.toList());
        };*/
        //这里下面一行代码和上面注释掉的代码一个意思,只是写法更简洁
        BiFunction<Integer,List<Person>,List<Person>> biFunction = (ageOfPerson,personList) ->
            personList.stream().filter(person -> person.getAge() > ageOfPerson).collect(Collectors.toList());

        return biFunction.apply(age,list);
    }

    public static <T> List<T> getPersonByAge2(int age,List<T> list,BiFunction<Integer,List<T>,List<T>> biFunction){
        return biFunction.apply(age,list);
    }
}

BinaryOperator<T>(BiFunction子接口)

参数为T,对参数为T得对象进行二元操作,并返回T类型得结果,其中方法为T apply(T t1, T t2)

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

下面看一个简单得例子:

public class BinaryOperatorTest {

    public static void main(String[] args) {
        Integer compute = compute(3, 5, (value1, value2) -> value1 + value2);
        System.out.println(compute);

        String min1 = getShortMin("hello", "world", (value1, value2) -> value1.compareTo(value2));
        System.out.println(min1);
        String min2 = getShortMin("hello", "world", (value1, value2) -> value1.charAt(0) - value2.charAt(0));
        System.out.println(min2);

        String max = getShortMax("hello", "world", (value1, value2) -> value1.charAt(0) - value2.charAt(0));
        System.out.println(max);

    }

    public static <T> T compute(T a, T b, BinaryOperator<T> binaryOperator){
        return binaryOperator.apply(a,b);
    }

    /**
     * 返回两个数的较小值
     * @param a
     * @param b
     * @param comparator
     * @param <T>
     * @return
     */
    public static <T> T getShortMin(T a, T b, Comparator<T> comparator){
        return BinaryOperator.minBy(comparator).apply(a,b);
    }

    /**
     * 返回两个数的较大值
     * @param a
     * @param b
     * @param comparator
     * @param <T>
     * @return
     */
    public static <T> T getShortMax(T a, T b, Comparator<T> comparator){
        return BinaryOperator.maxBy(comparator).apply(a,b);
    }
}

Comparator<T>接口

@FunctionalInterface
public interface Comparator<T> {
   
    int compare(T o1, T o2);
}

下面看一个简单得例子:

public class StringComparator {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("zhangsan","wangwu","lisi","zhaoliu");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        System.out.println(list);

        Collections.sort(list,(o1,o2) -> {
            return o2.compareTo(o1);
        });
        System.out.println(list);

        //expression o2.compareTo(o1)
        //statement {return o2.compareTo(o1);}

        Collections.sort(list,(o1,o2) -> o2.compareTo(o1));
        System.out.println(list);

        Collections.sort(list,Comparator.reverseOrder());
        System.out.println(list);
    }
}

Runnable接口

@FunctionalInterface
public interface Runnable {
  
    public abstract void run();
}

下面看一个简单得例子:

new Thread(() -> System.out.println("hello world")).start();

除了上述得类型得接口外还有其他的一些接口供我们使用:

1).BiFunction<T, U, R>

参数类型有2个,为T,U,返回值为R,其中方法为R apply(T t, U u)

2).UnaryOperator<T>(Function子接口)

参数为T,对参数为T的对象进行一元操作,并返回T类型结果,其中方法为T apply(T t)

3).BinaryOperator<T>(BiFunction子接口)

参数为T,对参数为T得对象进行二元操作,并返回T类型得结果,其中方法为T apply(T t1, T t2)

4).BiConsumcr(T, U)

参数为T,U无返回值,其中方法为 void accept(T t, U u)

5).ToIntFunction<T>、ToLongFunction<T>、ToDoubleFunction<T>

参数类型为T,返回值分别为int,long,double,分别计算int,long,double得函数。

6).IntFunction<R>、LongFunction<R>、DoubleFunction<R>

参数分别为int,long,double,返回值为R。

以上就是java8内置得核心函数式接口,其中包括了大部分得方法类型,所以可以在使用得时候根据不同得使用场景去选择不同得接口使用。

这里的函数式接口可以理解成,定义了很多不同的参数个数待实现接口。

Java8内置了很多函数式接口(在java.util.function包下的接口),下面表格有列出:

接口 方法 参数 返回值
Function R apply(T t) 一个参数 R
DoubleFunction R apply(double value) 一个参数 R
IntFunction R apply(int value) 一个参数 R
UnaryOperator extends Function T apply(T t) 一个参数 T

接口 方法 参数 返回值
BiFunction R apply(T t, U u) 两个参数 R
BinaryOperator extends BiFunction T apply(T t, T u) 两个参数 T
DoubleBinaryOperator double applyAsDouble(double l, double r) 两个参数 double
DoubleBinaryOperator double applyAsDouble(double l, double r) 两个参数
double
DoubleUnaryOperator double applyAsDouble(double operand) 一个参数 double
IntBinaryOperator int applyAsInt(int left, int right) 两个参数 int
LongBinaryOperator long applyAsLong(long left, long right) 两个参数 long

接口 方法 参数 返回值
DoubleToIntFunction int applyAsInt(double value) 一个参数 int
IntToLongFunction long applyAsLong(int value) 一个参数 long
DoubleToLongFunction long applyAsLong(double value) 一个参数 long
IntToDoubleFunction double applyAsDouble(int value) 一个参数 double

接口 方法 参数 返回值
Predicate boolean test(T t) 一个参数 boolean
BiPredicate boolean test(T t, U u) 两个参数 boolean
DoublePredicate boolean test(double value) 一个参数 boolean
IntPredicate boolean test(int value) 一个参数 boolean

接口 方法 参数 返回值
Supplier T get() T
BooleanSupplier boolean getAsBoolean() boolean
DoubleSupplier double getAsDouble() boolean
IntSupplier int getAsInt() boolean

接口 方法 参数 返回值
Consumer void accept(T t) 一个参数 void
BiConsumer void accept(T t, U u) 两个参数 void
DoubleConsumer void accept(double value) 一个参数 void
IntConsumer void accept(int value) 一个参数 void
ObjDoubleConsumer void accept(T t, double value) 两参数 void

上面为Java 8 中函数式接口列表

这里给出一份较为全的函数式接口与描述符对应的接口声明列表:

函数式接口 函数描述符
Predicate (T) -> boolean
Consumer (T) -> void
Function< T, R > (T) -> R
Supplier () -> T
UnaryOperator (T) -> T
BinaryOperator (T, T) -> T
BiPredicate (L, R) -> boolean
BiConsumer (T, U) -> void
BiFunction (T, U) -> R

如果同学们需要的函数式接口没有被覆盖,可以根据JDK中的声明来编写适合自己使用的函数式接口声明。

Optional

用于简化Java中对空值的判断处理,以防止出现各种空指针异常。
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。

Optional对象创建

它的构造函数都是private类型的,因此要初始化一个Optional的对象无法通过其构造函数进行创建。它提供了一系列的静态方法用于构建Optional对象:

empty

用于创建一个空的Optional对象;其value属性为Null。
如:

Optional o = Optional.empty();

of

根据传入的值构建一个Optional对象;
传入的值必须是非空值,否则如果传入的值为空值,则会抛出空指针异常。
使用:

o = Optional.of("test"); 

ofNullable

根据传入值构建一个Optional对象
传入的值可以是空值,如果传入的值是空值,则与empty返回的结果是一样的。

方法

Optional包含以下方法:

方法名 说明
get 获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值。
isPresent Value是否为空值的判断;
ifPresent 当Value不为空时,执行传入的Consumer;
ifPresentOrElse Value不为空时,执行传入的Consumer;否则执行传入的Runnable对象;
filter 当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象
map 一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象;
flatMap 一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象)
or 如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象;
stream 如果Value为空,返回Stream对象的Empty值;否则返回 Stream.of(value)的Stream对象;
orElse Value不为空则返回Value,否则返回传入的值;
orElseGet Value不为空则返回Value,否则返回传入的Supplier生成的值;
orElseThrow Value不为空则返回Value,否则抛出Supplier中生成的异常对象;

使用场景

常用的使用场景如下:

判断结果不为空后使用

如某个函数可能会返回空值,以往的做法:

String s = test();
if (null != s) {
    System.out.println(s);
}

现在的写法就可以是:

Optional<String> s = Optional.ofNullable(test());
s.ifPresent(System.out::println);

乍一看代码复杂度上差不多甚至是略有提升;那为什么要这么做呢?
一般情况下,我们在使用某一个函数返回值时,要做的第一步就是去分析这个函数是否会返回空值;如果没有进行分析或者分析的结果出现偏差,导致函数会抛出空值而没有做检测,那么就会相应的抛出空指针异常!
而有了Optional后,在我们不确定时就可以不用去做这个检测了,所有的检测Optional对象都帮忙我们完成,我们要做的就是按上述方式去处理。

变量为空时提供默认值

如要判断某个变量为空时使用提供的值,然后再针对这个变量做某种运算;
以往做法:

if (null == s) {
    s = "test";
}
System.out.println(s);

现在的做法:

Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElse("test"));

变量为空时抛出异常,否则使用

以往写法:

if (null == s) {
    throw new Exception("test");
}
System.out.println(s);

现在写法:

Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElseThrow(()->new Exception("test")));

github源码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容