初次定义自己的函数式接口:
package top.hengshare.interviewer.java8.lambda; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class HuanRao { /** * 记得行为参数化 * @return * @throws IOException */ public static String processFile() throws IOException { try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){ return br.readLine(); } } /** * 执行一个行为 * @param bufferedReaderProcessor * @return * @throws IOException */ public static String processFile(BufferedReaderProcessor bufferedReaderProcessor) throws IOException{ try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){ return bufferedReaderProcessor.process(br); } } public static void main(String[] args) throws IOException { String result = HuanRao.processFile(); } } /** * 使用函数式接口传递行为 * 接口只包含一个抽象方法,此实例的函数描述符为 (BufferedReader) -> String * 反应到函数定义就是:返回值为String,函数的参数为BuffereReader类型 */ @FunctionalInterface interface BufferedReaderProcessor{ String process(BufferedReader b)throws IOException; public static void main(String[] args) throws IOException { //传递lambda表达式 HuanRao.processFile(BufferedReader::readLine); HuanRao.processFile((BufferedReader br) -> br.readLine()+br.readLine()); } }上面是我们自定义的函数式接口,现在,我们使用Java8中提供的几个函数式接口来完成一些功能:
package top.hengshare.interviewer.java8.lambda; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; /** * 在 @{HuaRao}中我们发现函数式接口的定义很简单,所以应该是可以共用的。 * Java8中,已经提供了几个常用的函数式接口供我们使用,比如: * Comparable, Runnable, Callable, Predicate, Consumer, Function等 * 下面我们来使用这些函数式接口 */ public class Java8Interface { /** * java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型 * T对象,并返回一个boolean。 * @param list * @param p * @param <T> * @return */ public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = Lists.newArrayList(); for (T s : list) { if (p.test(s)) { results.add(s); } } return results; } /** * java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T * 的对象,没有返回( void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用 * 这个接口。 * * 你可以用它来创建一个forEach方法,接受一个Integers的列表,并对其中 * 每个元素执行操作 * * @param list * @param consumer * @param <T> */ public static <T> void forEach(List<T> list, Consumer<T> consumer) { for (T i: list) { consumer.accept(i); } } /** * java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个 * 泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射 * 到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)。 * * 在下面的代码中,我们向你展示如何利用它来创建一个map方法,以将一个String列表映射到包含每个 * String长度的Integer列表。 * @param list * @param f * @param <T> * @param <R> * @return */ public static <T, R> List<R> map(List<T> list, Function<T, R> f){ ArrayList<R> result = Lists.newArrayList(); for (T t : list) { result.add(f.apply(t)); } return result; } public static void main(String[] args) { List<String> listOfString = Lists.newArrayList(); Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> filter = filter(listOfString, nonEmptyStringPredicate); System.out.println(filter); forEach(Arrays.asList(1,2,3,4,5), (System.out::println)); List<Integer> map = map(Arrays.asList("lambdas", "in", "action"), (String::length)); System.out.println(map); } }尽量不要使用局部变量,如果使用局部变量,那得把局部变量设置为final的,原因就是局部变量是存放在栈中的,存放在栈中的变量只能被读取,不能被改变,这类似于Java中的final限定符,为了在编译的时候可以检查出来这个错误,我们应该在lambda表达式使用到局部变量的地方将局部变量使用final进行限制。
对于实例变量的使用,我们可以正常使用,因为实例变量存放于内存的堆中,堆中的变量是可以被读取和改变的。这类似于Java中将对象引用作为参数传递到一个方法中的用法。
方法引用我们可以把它看做是 仅仅调用特定方法的Lambda的一种快捷写法。 实例如下:
(Apple a) -> a.getWeight() Apple::getWeight () -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack (str, i) -> str.substring(i) String::substring (String s) -> System.out.println(s) System.out::println方法引用可以分为三类:
指向静态方法(args) -> ClassName.staticMethod(args) //可以写成 ClassName::staticMethod 指向实例对象的任意方法(arg0是ClassName类型的)(arg0, rest) -> arg0.instanceMethod(rest) //可以写成 ClassName::instanceMethod 指向实例对象的属性的方法引用(args) -> expr.instanceMethod(args) //可以写成 expr::instanceMethod下面是几个构造函数的例子,某些情况下,使用构造函数引用可以让代码简洁:
//不带参数的构造函数 Supplier<Apple> c1 = Apple::new; Apple a1 = c1.get(); //等价于 Supplier<Apple> c1 = () -> new Apple(); Apple a1 = c1.get(); //带参数的构造函数 Function<Integer, Apple> c2 = Apple::new; Apple a2 = c2.apply(110); //等价于 Function<Integer, Apple> c2 = (weight) -> new Apple(weight); Apple a2 = c2.apply(110); //带有两个参数的构造函数 iFunction<String, Integer, Apple> c3 = Apple::new; Apple c3 = c3.apply("green", 110); //等价于 BiFunction<String, Integer, Apple> c3 =(color, weight) -> new Apple(color, weight); Apple c3 = c3.apply("green", 110); //不将构造函数实例化,但可以引用它,这有一些有趣的应用,如下: static Map<String, Function<Integer, Fruit>> map = new HashMap<>(); static { map.put("apple", Apple::new); map.put("orange", Orange::new); // etc... } //利用构造函数引用,新创建了一个方法,用两个参数可以得到具有制定重量的不同水果 public static Fruit giveMeFruit(String fruit, Integer weight){ return map.get(fruit.toLowerCase()) .apply(weight); }在上面,我们分别对java8中的函数式,匿名类,lambda表达式,方法引用进行了详细的介绍,下面,我们分步骤,一步一步的综合起来看一下在实际的应用中我们可以怎么使用,以及为什么这么使用:
package top.hengshare.interviewer.java8.lambda; import com.google.common.collect.Lists; import java.util.Comparator; import java.util.List; /** * @author StivenYang * @program interview * @description Java8实战第一章实战代码 * @date 2019-07-22 23:55 **/ public class Practice01 { public static void main(String[] args) { List<Apple> appleList = Lists.newArrayList(); Apple green = new Apple("green"); green.setWeight(2D); appleList.add(green); Apple red = new Apple("red"); red.setWeight(3D); appleList.add(red); //第一步:传递代码 appleList.sort(new AppleComparator()); System.out.println(appleList); //第二步:使用匿名类 appleList.sort(new Comparator<Apple>() { @Override public int compare(Apple o1, Apple o2) { return o1.getWeight().compareTo(o2.getWeight()); } }); System.out.println(appleList); //第三部:使用lambda表达式 appleList.sort((Apple o1, Apple o2) -> o1.getWeight().compareTo(o2.getWeight())); System.out.println(appleList); //因为jvm可以根据lambda上下文来推断lambda表达式的参数类型,所以还可以简化为下面这种写法 appleList.sort((o1, o2) -> o1.getWeight().compareTo(o2.getWeight())); System.out.println(appleList); //又因为java8的comparator接口中有comparing静态方法,它可以像下面这样用: appleList.sort(Comparator.comparing((apple -> apple.getWeight()))); System.out.println(appleList); //第四步:使用方法引用 appleList.sort(Comparator.comparing(Apple::getWeight)); System.out.println(appleList); } } class AppleComparator implements Comparator<Apple> { @Override public int compare(Apple a1, Apple a2){ return a1.getWeight().compareTo(a2.getWeight()); } }比较器复合,从表面上意思是多个lambda表达式连在一起使用,实际也是如此,这样我们可以根据对象的不同属性对列表中的对象进行排序或者做其他一些操作:
package top.hengshare.interviewer.java8.lambda; import com.google.common.collect.Lists; import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import static top.hengshare.interviewer.java8.lambda.Java8Interface.filter; public class Complex { public static void main(String[] args) { List<Apple> appleList = Lists.newArrayList(); Apple green = new Apple("green"); green.setWeight(2D); appleList.add(green); Apple red = new Apple("red"); red.setWeight(3D); appleList.add(red); //比较器复合 //好处:简单排序很方便 //逆序排序一个数组 appleList.sort(Comparator.comparing(Apple::getWeight).reversed()); //比较器链 appleList.sort(Comparator.comparing(Apple::getColor) .reversed() .thenComparing(Apple::getWeight)); //谓词复合 //谓词接口包含三个方法:and\or\negate,可以使用已经有的Predicate来创建更加复杂的谓词 //好处是:谓词复杂了,lambda表达式就可以写的比较简单,易读了 Predicate<Apple> redApple = apple -> "red".equals(apple.getColor()); List<Apple> redAppleList = filter(appleList, redApple); System.out.println(redAppleList); //不是红苹果 Predicate<Apple> notRedApple = redApple.negate(); List<Apple> notRedAppleList = filter(appleList, notRedApple); System.out.println(notRedAppleList); //红苹果,而且重量超过150g Predicate<Apple> andPredicate = redApple.and(apple -> apple.getWeight() > 150); List<Apple> andPredicateList = filter(appleList, andPredicate); System.out.println(andPredicateList); //要么是重150g以上的红苹果,要么是绿苹果 Predicate<Apple> or = redApple.and(apple -> apple.getWeight() > 150).or(apple -> "green".equals(apple.getColor())); List<Apple> result = filter(appleList, or); System.out.println(result); //函数复合 //好处是,可以重复利用我们定义好的函数,来拼装成为一个更加复杂的函数,类似于数学中的积分 //可以把Function接口所代表的Lambda表达式复合起来。 Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例 Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; //andThen: 下面类似于g(f(x)) Function<Integer, Integer> h = f.andThen(g); int function1 = h.apply(1); System.out.println(function1); //compose: 下面类似于f(g(x)) Function<Integer, Integer> k = f.compose(g); Integer function2 = k.apply(1); System.out.println(function2); } }有关函数复合的应用,我们可以用一个对静态方法的使用的过程来说明:我们有一个处理信封的工具类,这个工具类可以添加标题、添加结尾、以及检查语法。我们可以使用这三个方法来组装成不同的处理流水线来处理数据:
package top.hengshare.interviewer.java8.lambda; import java.util.function.Function; public class FunctionComplex { public static void main(String[] args) { String text = "我是一个普通的程序员,我喜欢敲代码,它可以让我实现自己的价值,帮助这个社会变得更美好。\n我最近在学习labda表达式,他真是太方便,太强大了!"; Function<String, String> addHeader = Letter::addHeader; Function<String, String> resultFunction = addHeader.andThen(Letter::addFooter) .andThen(Letter::checkSpelling); String result = resultFunction.apply(text); System.out.println(result); } } class Letter{ public static String addHeader(String text){ return "亲爱的xxx: \n " + text; } public static String addFooter(String text){ return text + " \n 此致!敬礼!"; } public static String checkSpelling(String text){ return text.replaceAll("labda", "lambda"); } }下面用lambda表达式完成一个简单的积分计算:
设计函数的调用形式,根据调用形式来确定函数的描述符根据需求,完成函数的定义 package top.hengshare.interviewer.java8.lambda; import java.util.function.DoubleFunction; public class Integral { public static void main(String[] args) { //1. 对线性方程y=x+10求积分,函数调用的时候是这样 g(f, a, b),其中,a和b是积分的上下区间 //f的描述符应该是double->double,符合的有DoubleFunction<Double>接口 double result = Integral.integral((double x) -> x + 10, 3, 7); System.out.println(result); } //2. 完成函数的定义 public static double integral(DoubleFunction<Double> f, double a, double b){ //上底加下底乘高除以2是梯形的面积 return (f.apply(a)+f.apply(b))*(b-a)/2.0; } }