| 
 函数式接口  
概念:有且仅有一个抽象方法的接口。  
格式:  
修饰符 interface 接口名称 {  
    public abstract 返回值类型 方法名称(可选参数信息);  
    // 其他非抽象方法内容  
}  
@FunctionalInterface注解  
与@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:  
强制检查该接口是否确实有且仅有一个抽象方法,不是将会报错  
@FunctionalInterface  
public interface MyFunctionalInterface {  
        void myMethod();  
}  
自定义函数式接口  
public class Demo09FunctionalInterface {          
        // 使用自定义的函数式接口作为方法参数  
        private static void doSomething(MyFunctionalInterface inter) {  
                inter.myMethod(); // 调用自定义的函数式接口方法  
        }  
 
        public static void main(String[] args) {  
                // 调用使用函数式接口的方法  
                doSomething(() -> System.out.println("Lambda执行啦!"));  
        }  
}  
 
函数式编程  
Lambda的延迟执行  
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。  
将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。  
使用Lambda作为参数和返回值  
 
public class Demo04Runnable {  
    private static void startThread(Runnable task) {  
              new Thread(task).start();  
    }  
 
    public static void main(String[] args) {  
              startThread(()-> System.out.println("线程任务执行!")); // 参数为lambda表达式  
    }  
}  
 
当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调该方法获取:  
import java.util.Arrays;  
import java.util.Comparator;  
 
public class Demo06Comparator {  
    private static Comparator<String> newComparator() {  
              return (a, b) -> b.length() - a.length();  
    }  
 
    public static void main(String[] args) {  
        String[] array = { "abc", "ab", "abcd" };  
        System.out.println(Arrays.toString(array));  
        Arrays.sort(array, newComparator());  
        System.out.println(Arrays.toString(array));  
    }  
}  
 
常用函数式接口  
Supplier接口  
java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。  
import java.util.function.Supplier;  
 
public class Demo08Supplier {  
    private static String getString(Supplier<String> function) {  
              return function.get();  
    }  
 
    public static void main(String[] args) {  
        String msgA = "Hello";  
        String msgB = "World";  
        System.out.println(getString(() -> msgA + msgB));  
    }  
}  
 
练习:求数组元素最大值  
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值  
接口的泛型:Integer类。  
public class Demo02Test {  
    //定一个方法,方法的参数传递Supplier,泛型使用Integer  
    public static int getMax(Supplier<Integer> sup){  
        return sup.get();  
    }  
 
    public static void main(String[] args) {  
        int arr[] = {2,3,4,52,333,23};  
 
        //调用getMax方法,参数传递Lambda  
        int maxNum = getMax(()->{  
           //计算数组的最大值  
           int max = arr[0];  
           for(int i : arr){  
               if(i>max){  
                   max = i;  
               }  
           }  
           return max;  
        });  
        System.out.println(maxNum);  
    }  
}  
 
Consumer接口  
java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。  
抽象方法:accept  
import java.util.function.Consumer;  
 
public class Demo09Consumer {  
    private static void consumeString(Consumer<String> function) {  
              function.accept("Hello");  
    }  
 
    public static void main(String[] args) {  
        consumeString(s -> System.out.println(s));  
    }  
}  
默认方法:andThen  
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen。  
default Consumer<T> andThen(Consumer<? super T> after) {  
    Objects.requireNonNull(after);  
    return (T t) -> { accept(t); after.accept(t); };  
}  
备注:java.util.Objects的requireNonNull静态方法将会在参数为null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦。  
andThen:  
import java.util.function.Consumer;  
 
public class Demo10ConsumerAndThen {  
    private static void consumeString(Consumer<String> one, Consumer<String> two) {  
              one.andThen(two).accept("Hello");  
    }  
 
    public static void main(String[] args) {  
        consumeString(  
            s -> System.out.println(s.toUpperCase()),  
            s -> System.out.println(s.toLowerCase()));  
    }  
}  
运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。  
练习:格式化打印信息  
下面的字符串数组当中存有多条信息,请按照格式“姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起。  
public static void main(String[] args) {  
          String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };  
}  
解答  
import java.util.function.Consumer;  
 
public class DemoConsumer {  
    public static void main(String[] args) {  
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };  
        printInfo(s -> System.out.print("姓名:" + s.split(",")[0]),  
                  s -> System.out.println("。性别:" + s.split(",")[1] + "。"),  
                  array);  
    }  
 
    private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {  
        for (String info : array) {  
            one.andThen(two).accept(info); // 姓名:迪丽热巴。性别:女。  
        }  
    }  
}  
Predicate接口  
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口。  
抽象方法:test  
Predicate接口中包含一个抽象方法:boolean test(T t)。用于条件判断的场景:  
import java.util.function.Predicate;  
 
public class Demo15PredicateTest {  
    private static void method(Predicate<String> predicate) {  
        boolean veryLong = predicate.test("HelloWorld");  
        System.out.println("字符串很长吗:" + veryLong);  
    }  
 
    public static void main(String[] args) {  
        method(s -> s.length() > 5);  
    }  
}  
默认方法:and  
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法and。其JDK源码为:  
default Predicate<T> and(Predicate<? super T> other) {  
    Objects.requireNonNull(other);  
    return (t) -> test(t) && other.test(t);  
}  
 
如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:  
import java.util.function.Predicate;  
 
public class Demo16PredicateAnd {  
    private static void method(Predicate<String> one, Predicate<String> two) {  
        boolean isValid = one.and(two).test("Helloworld");  
        System.out.println("字符串符合要求吗:" + isValid);  
    }  
 
    public static void main(String[] args) {  
        method(s -> s.contains("H"), s -> s.contains("W"));  
    }  
}  
 
默认方法:or  
与and的“与”类似,默认方法or实现逻辑关系中的“或”。JDK源码为:  
default Predicate<T> or(Predicate<? super T> other) {  
    Objects.requireNonNull(other);  
    return (t) -> test(t) || other.test(t);  
}  
如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:  
import java.util.function.Predicate;  
 
public class Demo16PredicateAnd {  
    private static void method(Predicate<String> one, Predicate<String> two) {  
        boolean isValid = one.or(two).test("Helloworld");  
        System.out.println("字符串符合要求吗:" + isValid);  
    }  
 
    public static void main(String[] args) {  
        method(s -> s.contains("H"), s -> s.contains("W"));  
    }  
}  
 
默认方法:negate  
默认方法negate的JDK源代码为:  
default Predicate<T> negate() {  
    return (t) -> !test(t);  
}  
从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在test方法调用之前调用negate方法,正如and和or方法一样:  
import java.util.function.Predicate;  
 
public class Demo17PredicateNegate {  
    private static void method(Predicate<String> predicate) {  
        boolean veryLong = predicate.negate().test("HelloWorld");  
        System.out.println("字符串很长吗:" + veryLong);  
    }  
 
    public static void main(String[] args) {  
              method(s -> s.length() < 5);  
    }  
}  
 
练习:集合信息筛选  
数组当中有多条“姓名+性别”的信息如下,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,需要同时满足两个条件:  
1.必须为女生; 
2.姓名为4个字。 
 
public class DemoPredicate {  
    public static void main(String[] args) {  
              String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };  
    }  
}  
解答  
import java.util.ArrayList;  
import java.util.List;  
import java.util.function.Predicate;  
 
public class DemoPredicate {  
    public static void main(String[] args) {  
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };  
        List<String> list = filter(array,  
                                   s -> "女".equals(s.split(",")[1]),  
                                   s -> s.split(",")[0].length() == 4);  
        System.out.println(list);  
    }  
 
    private static List<String> filter(String[] array, Predicate<String> one,   
                                       Predicate<String> two) {  
        List<String> list = new ArrayList<>();  
        for (String info : array) {  
            if (one.and(two).test(info)) {  
                list.add(info);  
            }  
        }  
        return list;  
    }  
}  
Function接口  
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。  
抽象方法:apply  
Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。  
使用的场景例如:将String类型转换为Integer类型  
import java.util.function.Function;  
 
public class Demo11FunctionApply {  
    private static void method(Function<String, Integer> function) {  
        int num = function.apply("10");  
        System.out.println(num + 20);  
    }  
 
    public static void main(String[] args) {  
        method(s -> Integer.parseInt(s));  
    }  
}  
默认方法:andThen  
Function接口中有一个默认的andThen方法,用来进行组合操作。JDK源代码如:  
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {  
    Objects.requireNonNull(after);  
    return (T t) -> after.apply(apply(t));  
}  
该方法同样用于“先做什么,再做什么”的场景,和Consumer中的andThen差不多:  
import java.util.function.Function;  
 
public class Demo12FunctionAndThen {  
    private static void method(Function<String, Integer> one, Function<Integer, Integer> two) {  
        int num = one.andThen(two).apply("10");  
        System.out.println(num + 20);  
    }  
 
    public static void main(String[] args) {  
        method(str->Integer.parseInt(str)+10, i -> i *= 10);  
    }  
}  
第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过andThen按照前后顺序组合到了一起。  
请注意,Function的前置条件泛型和后置条件泛型可以相同。  
练习:自定义函数模型拼接  
 
请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:  
         String str = "赵丽颖,20";  
1.将字符串截取数字年龄部分,得到字符串; 
2.将上一步的字符串转换成为int类型的数字; 
3.将上一步的int数字累加100,得到结果int数字。 
解答  
import java.util.function.Function;  
 
public class DemoFunction {  
    public static void main(String[] args) {  
        String str = "赵丽颖,20";  
        int age = getAgeNum(str, s -> s.split(",")[1],  
                            s ->Integer.parseInt(s),  
                            n -> n += 100);  
        System.out.println(age);  
    }  
 
    private static int getAgeNum(String str, Function<String, String> one,  
                                 Function<String, Integer> two,  
                                 Function<Integer, Integer> three) {  
        return one.andThen(two).andThen(three).apply(str);  
    }  
}  
 
 
 
 
 |