Java函数接口
函数接口概述
什么是函数接口:函数接口就是只定义一个抽象方法的接口,但同时可以拥有默认方法,为了简化函数方法的实现可以使用lambda表达式,其基本语法如下:
(parameters) -> expression # 控制流语句需要使用{}
Java函数编程:原始类型转换成对应的引用类型,Java中的泛型只能绑定引用类型,这由泛型内部的实现方式造成的,Java中包括一个将原始基本类型转换成引用类型的机制,成为装箱,反过程为拆箱操作。IntPredicate等可以避免自动装箱操作,对于专门的输入参数类型的函数式接口,如DoublePredicate,IntConsumer,Function输出参数的变种为ToIntFunction和IntToDoubleFunction
如下图的常见函数接口的表格(多看看函数接口源码熟练使用即可):
Function接口
1、Function 接口的作用
代表一个方法,可以接收一个参数,并产生一个结果,泛型参数T代表输入参数类型,R代表产生出参J数类型
2、Function 接口的实现
(因为函数编程参数即是动作,接下来介绍的时候对Function称之为操作)。函数接口由@FcuntionalInterface注解修饰,并且只能有一个接口参数,但可以同时包括多个default函数。如下Function接口的源码:
@FunctionalInterface
public interface Function<T, R> {
// 该函数标识对参数T使用Function进行处理,产生R类型的结果
R apply(T t);
// 例如 A.compose(B).apply(v);先对v使用B操作,再进行A操作。A为调用者
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 例如 A.andThen(B).apply(v);对v先进行A操作,再进行B操作
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
} // 返回一个总是返回它的输入参数的Function,此时结果和输入参数均为T
static <T> Function<T, T> identity() {
return t -> t;
}
}
3、应用
1、Function使用案例(基于Java8的网络爬虫):
当使用Java中国的OkHttp网络框架和Jsoup框架处理网络爬虫的时候,常常会根据不同的平台对不同页面进行处理,所以为了规范化以及行为参数化处理,我这里以此为背景,写了一个Function类型的HTTP超文本标签id为某某某的属性解析器。
// Function函数结构规范
private static String parserHtmlBy(Document document, Function<Document, String> idFunction) {
if (Objects.isNull(idFunction) || Objects.isNull(document)) {
return "";
}
// 此处使用了apply函数
return idFunction.apply(document);
}
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
String uri = "https://www.bilibili.com/";
Response httpBody = client.newCall(new Request.Builder().url(uri)
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36")
.build()).execute();
if (!httpBody.isSuccessful()) {
throw new HttpRetryException("Http Request Exception", httpBody.code());
}
Document html = Jsoup.parse(httpBody.body().string());
// 不同的页面可能使用Function不一致,所以这里使用Function对函数进行封装
// 行为参数化,行为idFunction对html参数进行处理
String headWrapper = parserHtmlBy(html, document -> {
Element idToValue = html.getElementById("reportFirst1");
Map<String, String> attrs = Maps.newHashMap();
Optional.ofNullable(idToValue).ifPresent(t -> {
idToValue.attributes().forEach(attribute -> {
attrs.put(attribute.getKey(), attribute.getValue());
});
});
return new GsonBuilder().create().toJson(attrs);
});
System.out.println("哔哩哔哩 " + headWrapper);
}
2、关于compose,andThen,identity函数的研究(请看一下源码中的Doc分析)
Function<Integer,Integer> multi = i->i*i;
Function<Integer,Integer> resize = i->i*2;
// 执行顺序,resize,multi。结果为16
System.out.println(multi.compose(resize).apply(2));
// 执行顺序,multi,resize。结果为8
System.out.println(multi.andThen(resize).apply(2));
// 直接输出原值。结果为2
System.out.println(Function.identity().apply(2));
Predicate接口
1、predicate函数结构作用
代表一个方法,可以接收一个参数,并根据某一规则判断,该参数是否符合该规则,根据规则过滤数据信息。
2、predicate函数接口实现
(因为函数编程参数即是动作,接下来介绍的时候对Predicate称之为操作)。函数接口由@FunctionalInterface注解修饰,并且只能有一个接口参数,但可以同时包括多个default函数。如下Predicate接口的源码:
@FunctionalInterfacepublic
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);
}
// 判断两个对象是否相等,Predicate接口的静态方法
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)? Objects::isNull : object -> targetRef.equals(object);
}
}
3、predicate函数接口使用(选茶叶)
public class TeaFilter {
@Data
@ToString
@AllArgsConstructor
static class Tea {
String type;
String price;
}
private static List<Tea> filter(List<Tea> teas, Predicate<Tea> t) {
List<Tea> suit = Lists.newLinkedList();
for (Tea tea : teas) {
// negate
if (t.test(tea)) {
suit.add(tea);
}
}
return suit;
}
public static void main(String[] args) {
List<Tea> teas = Arrays.asList(new Tea("龙绿茶", "100"),
new Tea("龙井", "1000"),
new Tea("信阳毛尖", "10000")
);
// test操作
List<Tea> tea = filter(teas, x -> x.getType().endsWith("毛尖"));
// or 操作
Predicate<Tea> teaOrPredicate = x -> x.getType().startsWith("龙");
Predicate<Tea> teaOrPredicateOther = x -> x.getPrice().compareTo("10") >= 0;
filter(teas, teaOrPredicate.or(teaOrPredicateOther)).forEach(x -> System.out.println(x.toString()));
System.out.println("==========");
// and 操作
Predicate<Tea> teaAndPredicate = x -> x.getType().startsWith("龙");
Predicate<Tea> teaAndPredicateOther = x -> x.getPrice().compareTo("1000") >= 0;
filter(teas, teaAndPredicate.and(teaAndPredicateOther)).forEach(x -> System.out.println(x.toString()));
// 比较对象isEqual,可用于对象比较,但是不建议
Predicate<Tea> isEqual = Predicate.isEqual(new Tea("普洱", "100"));
System.out.println(isEqual.test(new Tea("普洱", "1000")));
}
}
Suppiler接口
1、Suppiler函数接口作用
Suppiler函数接口的作用,根据提供者返回结果,函数签名为 ()->T
2、Suppiler函数接口源码
// 返回一个提供者的结果
@FunctionalInterface
public interface Supplier<T> {
// 返回一个结果
T get();
}
3、举例子(一个对象的某个字段的值的获取和默认值):
publicstatic String suppiler(Supplier<String> suppiler, String defaultValue) {
String val = suppiler.get();
if (val == null || "".equals(val)) {
return defaultValue;
}
return val;
}
private static String sval(Supplier<String> suppiler) {
return suppiler(suppiler, "NA");
}
// 可以是任意一个引用类型,此时返回一个值
public static void main(String[] args) {
System.out.println(sval(new Node("")::getName));
}
Consumer接口
1、Consumer接口作用
java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void),可以简单的理解为消费者。
2、Consumer函数接口实现
// 与其他函数接口不同的是,Consumer接口操作产生副作用
// T 代表输入参数类型
@FunctionalInterface
public interface Consumer<T> {
// 对给定参数执行操作,没有返回值
void accept(T t);
// 返回组成的{@code Consumer},依次执行此操作
// 操作,然后进行{@code after}操作。如果执行任何一个操作会引发异常,它将被中继给
// 组合操作。如果执行此操作会引发异常,{@code after}操作将不会执行。
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
3、Consumer函数接口使用
public static void consumer(List<String> list, Consumer<String> consumer){
for(String s : list){
consumer.andThen(consumer).accept(s);
}
}
public static void main(String[] args) {
List<String> costMoney = new ArrayList<>();
costMoney.add("dollars"); // 美元
costMoney.add("yen"); // 日元
costMoney.add("RMB"); // 人民币
consumer(costMoney, System.out::println);
}