目录
1.1 新的函数式接口
1.1.1 Predicate
1.1.2 Consumer
1.1.3 Function
2.1 Lambda及函数式接口的例子
3.1 Lambda的方法引用
4.1 构造函数引用
5.1 复合 Lambda 表达式
5.1.1 比较器复合
5.1.2 谓词复合
5.1.3 函数复合
@FunctionalInterface
public interface Predicate<T>{ boolean test(T t);
}
示例:
public class Demo {public static void main(String[] args) {List<String> listOfStrings = new ArrayList<>();listOfStrings.add("hello");listOfStrings.add("world.");listOfStrings.add("");Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);System.out.println(nonEmpty);// List<String> nonEmpty = listOfStrings.stream()// .filter(item -> !item.isEmpty())// .List());}public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();for (T s : list) {if (p.test(s)) {results.add(s);}}return results;}
}
java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。比如,你可以用它来创建一个forEach方法,接受一个Integers的列表,并对其中每个元素执行操作。在下面的代码中,你就可以使用这个forEach方法,并配合Lambda来打印列表中的所有元素。
@FunctionalInterface
public interface Consumer<T>{void accept(T t);
}
示例:
public class Demo {public static void main(String[] args) {List<String> listOfStrings = new ArrayList<>();listOfStrings.add("hello");listOfStrings.add("world.");listOfStrings.add("");forEach(listOfStrings, System.out::println);}public static <T> void forEach(List<T> list, Consumer<T> c) {for (T i : list) {c.accept(i);}}}
其中,Lambda是Consumer中accept方法的实现。
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)。在下面的代码中,我们向你展示如何利用它来创建一个map方法,以将一个String列表映射到包含每个 String长度的Integer列表。
@FunctionalInterface
public interface Function<T, R>{R apply(T t);
}
示例:
public class Demo {public static void main(String[] args) {List<String> listOfStrings = new ArrayList<>();listOfStrings.add("hello");listOfStrings.add("world");listOfStrings.add("!!");System.out.println(map(listOfStrings, String::length));}public static <T, R> List<R> map(List<T> list,Function<T, R> f) {List<R> result = new ArrayList<>();for (T s : list) {result.add(f.apply(s));}return result;}
}
其中,Lambda是Function接口的apply方法的实现。
使用案例 | Lambda的例子 | 对应的函数式接口 |
布尔表达式 | (List<String> list) -> list.isEmpty() | Predicate<List<String>> |
创建对象 | ()->new Apple(10) | Supplier<Apple> |
消费一个对象 | (Apple a) -> System.out.Weight()) | Consumer<Apple> |
从一个对象中选择/提取 | (String s) -> s.length() | Function<String, Integer> 或 |
合并两个值 | (int a, int b) -> a * b | IntBinaryOperator |
比较两个对象 | (Apple a1, Apple a2) -> a1.getWeight()Weight()) | Comparator<Apple> 或 ToIntBiFunction<Apple, Apple> |
Lambda的方法引用主要有三类。
第二种和第三种方法引用可能乍看起来有点儿晕。类似于String::length的第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如,Lambda表达式(String s) -> s.toUppeCase()可以写作String::toUpperCase。但第三种方法引用指的是,你在Lambda中调用一个已经存在的外部对象中的方法。例如,Lambda表达式
()-&Value()可以写作expensiveTransaction::getValue。
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 |
对于一个现有构造函数,我们可以使用ClassName::new这样的格式来创建对象的引用。它的功能与指向静态方法的引用类似。例如:
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
其中,构造函数引用指向默认的Apple()构造函数,然后调用Supplier的get方法将产生一个新的Apple。这就等价于:
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
如果构造函数有一个参数,像Apple(Integer weight),那么它就适合Function接口的签名:
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
以上代码等价于:
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
下面简单应用一下上面的例子,假如我们要得到了一个具有不同重量的苹果的List,可以这样做:
// 苹果重量List<Integer> weights = Arrays.asList(7, 3, 4, 10);List<Apple> apples = map(weights, Apple::new);public static List<Apple> map(List<Integer> list,Function<Integer, Apple> f) {List<Apple> result = new ArrayList<>();for (Integer e : list) {result.add(f.apply(e));}return result;}
如果构造函数有两个参数,像Apple(String color, Integer weight),那么它就适合BiFunction接口的签名:
BiFunction<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);
不将构造函数实例化却能够引用它,这个功能有一些有趣的应用。例如,你可以使用Map来将构造函数映射到字符串值。
public class Demo {static Map<String, Function<String, Animal>> map = new HashMap<>();static {map.put("animal", Animal::new);// }public static void main(String[] args) {Animal animal = giveMyAnimal();System.out.println(animal);}public static Animal giveMyAnimal() {("animal").apply("大黄狗");}
}
下面做个小需求,我们根据实体对象的年龄进行排序(升序):
listOfAnimals.sort(Comparatorparing(Animal::getAge));
1.逆序
如果按照年龄逆序排序,我们可以这样:
listOfAnimals.sort(Comparatorparing(Animal::getAge).reversed());
2.比较器链
如果年龄一样的话,我们可以按照其他规则来排序,比如按照创建时间先后来排序:
listOfAnimals.sort(Comparatorparing(Animal::getAge).reversed().thenComparing(Animal::getCreateTime));
谓词接口包括三个方法:negate、and和or,让你可以重用已有的Predicate来创建更复杂的谓词。
比如,我们现在要筛选出要么是重(150克以上)的红苹果,要么是绿苹果的苹果集合:
Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150) .or(a -> "green".Color()));
我们还可以把Function接口所代表的Lambda表达式复合起来。Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。
1.andThen方法
andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数。
比如,假设有一个函数f给数字加1 (x -> x + 1),另一个函数g给数字乘2,你可以将它们组
合成一个函数h,先给数字加1,再给结果乘2:
// g(f(x))// f(x) = x +1 ; g(x) = x * 2;Function<Integer, Integer> f = x -> x + 1;Function<Integer, Integer> g = x -> x * 2;Function<Integer, Integer> h = f.andThen(g);int result = h.apply(1);System.out.println(result); // 输出4
2. compose方法
compose方法先把给定的函数用作compose的参数里面给的那个函数,然后再把函数本身用于结果。比如在上一个例子里用compose的话,它将意味着f(g(x)), 而andThen则意味着g(f(x)) 。
// f(g(x))// f(x) = x +1 ; g(x) = x * 2;Function<Integer, Integer> f = x -> x + 1;Function<Integer, Integer> g = x -> x * 2;Function<Integer, Integer> h = fpose(g);int result = h.apply(1);System.out.println(result); // 输出3
实际应用当中,比方说我们有一系列工具方法,在Letter类中用String表示的一封信做文本转换:
public class Letter{ public static String addHeader(String text){ return "From Raoul, Mario and Alan: " + text; } public static String addFooter(String text){ return text + " Kind regards"; } public static String checkSpelling(String text){ placeAll("labda", "lambda"); }
}
现在我们可以通过复合这些工具方法来创建各种转型流水线了,比如创建一个流水线:先加上
抬头,然后进行拼写检查,最后加上一个落款。
Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline = addHeader.andThen(Letter::checkSpelling) .andThen(Letter::addFooter);
第二个流水线可能只加抬头、落款,而不做拼写检查:
Function<String, String> addHeader = Letter::addHeader;Function<String, String> transformationPipeline= addHeader.andThen(Letter::addFooter);
(未完待续...)
本文发布于:2024-01-29 14:57:52,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170651147616089.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |