函数式编程
千呼万唤始出来,Java 8终于引入了对函数式编程的支持。函数式编程是一种编程范式,它将计算视为数学函数的计算,使代码更加简洁、易于理解和维护。
函数式编程
在java.util.function
中,包含了大量的函数式的接口。其中就有我们熟悉的Function
和Supplier
,这些基本上满足了开发日常的需要。这些接口中的每一个都是通用且抽象的,使得它们很容易适应几乎任何lambda表达式。
什么是函数接口
上面提到的接口有一个共性,那就是加上了@FunctionalInterface
且只有一个abstract方法
。比如
@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); };
}
}
查看jdk的文档可知,一个接口是函数式的需要满足特定的条件:该接口有且只有一个抽象方法。
关于函数接口需要注意以下几点
- 函数式接口是允许有
default method
的,因为default method
是有实现的不是abstract的 - 如果接口满足函数式接口的规则,那么就算没有
@FunctionalInterface
,它也被编译器当成函数式接口
类似于@Override注解,只要你的函数符合重载的要求,无论你是否标注了@Override,编译器都会识别这个重载函数
那么作为一个函数接口,它跟其他普通接口有什么优势或者区别呢?
- 函数接口只有一个抽象方法,如果接口标注了
@FunctionalInterface
,且有多个抽象方法,那编译报错 - 函数接口的实例可以通过Lambda表达式、方法引用和构造函数引用来创建
好了,到了函数接口创建的环节了,正好也可以看看Lambda表达式
、 方法引用
和构造函数引用
这些具体是什么。
函数接口的创建
Lambda表达式
Lambda表达式(lambda expressions)是Java 8最显著的函数式编程特性之一。它们允许您以更简洁的方式传递匿名函数,从而减少了冗余的代码。Lambda表达式的语法如下
(parameter_list) -> { body }
例如,对一个整数列表进行排序可以使用Lambda表达式如下:
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6, 5, 3);
numbers.sort((a, b) -> a.compareTo(b));
方法引用
方法引用(method references)是Lambda表达式的一种简写形式,它允许您直接引用现有方法或构造函数作为Lambda表达式。Java 8提供了四种类型的方法引用
- 静态方法引用: 使用
类名::静态方法名
的方式引用静态方法 - 实例方法引用: 使用
实例对象::实例方法名
的方式引用实例方法。 - 类的实例方法引用: 使用
类名::实例方法名
的方式引用类的任意对象的实例方法 - 构造函数引用: 使用
类名::new
的方式引用类的构造方法
例如
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println); // 实例方法引用
构造函数引用
构造函数引用(constructor references)是一种简化构造函数调用的方法,它允许您将构造函数本身作为Lambda表达式传递。构造函数引用主要用于以下情况:
- 创建新对象:您可以使用构造函数引用来创建新的对象,而无需显式地编写构造函数的调用。
- 与函数式接口结合:构造函数引用可以与函数式接口结合使用,例如
Supplier
,Function
等。
下面是构造函数引用的示例:
public class Person {
private String name;
public Person() {
this.name = "Unknown";
}
public Person(String name) {
this.name = name;
}
// Getters and setters
}
public class Main {
public static void main(String[] args) {
// 构造函数引用用于无参构造函数
Supplier<Person> personSupplier = Person::new;
Person person1 = personSupplier.get(); // 等同于 new Person()
// 构造函数引用用于有参构造函数
Function<String, Person> personFunction = Person::new;
Person person2 = personFunction.apply("John");
}
}
以上就是关于函数接口实例的创建了,既然java 8自带了些开箱即用的函数式接口,那我们就去瞅瞅呗
java函数式接口
java.util.function
包提供的函数式接口有很多,确实给开发带来了很大的便利。下面我们来讲下比较通用的几个,它们分别是Function
、Consumer
、Predicate
和Supplier
。
Function:接受参数,有返回参数
package com.linux.huhx.function;
import java.util.function.Function;
public class FunctionDemo {
private static int operateValue(int value, Function<Integer, Integer> function) {
return function.apply(value);
}
private static int operateValue(int value, Function<Integer, Integer> srcFunc, Function<Integer, Integer> destFunc) {
return srcFunc.andThen(destFunc).apply(value);
}
public static void main(String[] args) {
int value = 10;
int lambdaResult = operateValue(value, t -> t + 20);
System.out.println(lambdaResult); // 30
int andThenResult = operateValue(value, val -> val + 20, val -> val + 30);
System.out.println(andThenResult); // 60
}
}
Consumer:接受参数,没有返回
package com.linux.huhx.function;
import java.util.function.Consumer;
public class ConsumerDemo {
private static void printValue(String value, Consumer<String> consumer) {
consumer.accept(value);
}
public static void main(String[] args) {
printValue("huhx", s -> System.out.println(s.toUpperCase())); // HUHX
}
}
Predicate:接受参数,返回boolean
package com.linux.huhx.function;
import java.util.function.Predicate;
public class PredicateDemo {
private static boolean predicateValue(int value, Predicate<Integer> predicate) {
return predicate.test(value);
}
public static void main(String[] args) {
System.out.println(predicateValue(17, x -> x >= 18)); // false
}
}
Supplier:不接受参数,有返回值
package com.linux.huhx.function;
import java.util.function.Supplier;
import org.springframework.util.StringUtils;
public class SupplierDemo {
private static String getOrDefault(String value, Supplier<String> supplier) {
return StringUtils.isEmpty(value) ? supplier.get() : value;
}
public static void main(String[] args) {
String name = "";
System.out.println(getOrDefault(name, () -> "huhx")); // huhx
}
}
FAQ
Lambda为什么不能抛出异常?
Lambda表达式在Java 中不能抛出已检查异常(checked exception)的原因与Java 8引入Lambda表达式时的设计哲学有关,主要是为了简化函数式编程的语法和提高代码的可读性。