Java8之函数式编程
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
1.Lambda表达式
如下案例所示,分别创建三个接口,每个接口中只有一个方法,通过lambda表达式重写接口中的方法,可极大的简化代码。当只有一个入参时,则无需写 ()
当表达式只有一行时则无需写 {}
与 return
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunc1 {
/**
* 相当于接口实现类
* 当接口只有一个方法时可用Lambda表达式简写接口实现
*/
static Aa aa = () -> System.out.println("打印Aa");
static Ab ab = () -> "返回Ab";
static Ac ac = name -> name + ":说话";
static Ac ac2 = name -> {
System.out.println("入参:" + name);
return name + ": 我是Ac";
};
public static void main(String[] args) {
aa.testAa();
System.out.println(ab.testAb());
System.out.println(ac.testAc("kenewstar"));
System.out.println(ac2.testAc("k2"));
}
}
interface Aa {
void testAa();
}
interface Ab {
String testAb();
}
interface Ac {
String testAc(String name);
}
执行结果:
打印Aa
返回Ab
kenewstar:说话
入参:k2
k2: 我是Ac
任何 Lambda 表达式的基本语法是:
- 参数。 2. 接着
->
,可视为“产出”。 3. -> 之后的内容都是方法体。
2.方法引用
除了可以使用Lambda表达式重写接口方法外,还可使用方法引用的方式重写接口方法,使用前提也必须是该接口是一个函数式接口,即接口中只有一个方法(抽象方法),因为Java8允许接口中有静态方法和默认方法。
如下案例使用Lambda表达式与方法引用重写同一个方法。
/**
* 方法引用
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunc2 {
static String returnStr() {
return "call2";
}
static void printRunnable() {
System.out.println("打印Runnable2");
}
public static void main(String[] args) throws Exception {
// Callable函数式接口,
// Lambda表达式 无入参,有返参
Callable<String> c = () -> "call";
System.out.println(c.call());
// 方法引用重写接口
// 调用了一个只有返回值的方法
Callable<String> c2 = TestFunc2::returnStr;
System.out.println(c2.call());
// Runnable函数式接口
Runnable r = () -> System.out.println("打印Runnable");
r.run();
// 方法引用重写接口
// 调用了一个无入参,无返参的方法
Runnable r2 = TestFunc2::printRunnable;
r2.run();
}
}
执行结果:
call
call2
打印Runnable
打印Runnable2
注意 : 方法引用中重写的方法必须与函数式接口的方法类型格式保持一致,入参返参的顺序及类型都必须一致,才可使用该方法引用。
Runnable接口的一个简化示例
匿名内部类 -> lambda表达式 -> 方法引用
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunc3 {
static void run() {
System.out.println("方法引用");
}
public static void main(String[] args) {
// 使用匿名内部类的方式重写接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类");
}
}).start();
// 使用Lambda方式重写接口
new Thread(() -> System.out.println("Lambda表达式")).start();
// 使用方法引用的方式
new Thread(TestFunc3::run).start();
}
}
执行结果:
匿名内部类
Lambda表达式
方法引用
未绑定的方法引用
未绑定的方法引用是指没有关联对象的普通(非静态)方法。 使用未绑定的引用时,我们必须先提供对象:
我们先来看一个例子:
/**
* @author @author kenewstar
* @date 2022/01/23
*/
public class TestFun4 {
public static void main(String[] args) {
// Non-static method cannot be referenced from a static context
// TestFa fa = MethodRef::test;
TestFb fb = MethodRef::test;
MethodRef ref = new MethodRef();
System.out.println(fb.tf(ref));
// 或者使用 对象::方法的方式
TestFa fa = ref::test;
System.out.println(fa.tf());
}
}
class MethodRef {
public String test() {
return "test";
}
}
interface TestFa {
String tf();
}
interface TestFb {
String tf(MethodRef ref);
}
我们看到上面无法将 **MethodRef::test **函数式引用绑定到 TestFa 接口上,这是因为实际上还有另一个隐藏的参数: this 。 你不能在没有 MethodRef 对象的前提下调用 test() 。 因此,MethodRef::test 表示未绑定的方法引用,因为它尚未“绑定”到对象,如果使用 ref::test 方式则等同于已绑定的方法引用。
使用未绑定的引用时,函数式方法的签名(接口中的单个方法)不再与方法引用的签名完全匹配,因此需要一个额外的 MethodRef 参数对象。原因是:你需要一个对象来调用方法。
如果有多个参数,则默认第一个参数传递 this 对象
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunc4 {
public static void main(String[] args) {
TestFc fc = MethodRef::test;
MethodRef ref = new MethodRef();
System.out.println(fc.tf(ref, "other"));
}
}
class MethodRef {
public String test(String name) {
return "test + " + name;
}
}
interface TestFc {
String tf(MethodRef ref, String other);
}
构造函数引用
即通过构造方法的引用创建对象
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunc5 {
public static void main(String[] args) {
CreatePerson cp = Person::new;
Person kenewstar = cp.create("kenewstar", 22);
System.out.println(kenewstar);
}
}
interface CreatePerson {
Person create(String name, int age);
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
方法引用的三种方式:
类名::方法名
对象名::方法名
构造方法名:: new
3.函数式接口
@FunctionalInterface 一个被该注解标注的接口则表示它是一个函数式接口,该接口中只允许一个抽象方法的存在,存在多个则会编译不通过。例如前面例子中所提到的Runnable, Callable接口都是函数式接口
Java8提供函数式接口
特征 | 函数式方法名 | 示例 |
---|---|---|
无参数; 无返回值 | Runnable run() | Runnable |
无参数; 返回类型任意 | Supplier get() | Supplier BooleanSupplier IntSupplier LongSupplier DoubleSupplier |
无参数; 返回类型任意 | Callable call() | Callable |
1 个参数; 无返回值 | Consumer accept() | Consumer IntConsumer LongConsumer DoubleConsumer |
2 个参数 ;Consumer | BiConsumer accept() | BiConsumer<T, U> |
2 个参数 Consumer; 1 引用; 1 基本类型 | Obj类型Consumer accept() | ObjIntConsumer ObjLongConsumer ObjDoubleConsumer |
1 个参数; 返回类型不同 | Function apply() To类型 和 类型To类型 | Function IntFunction LongFunction DoubleFunction ToIntFunction ToLongFunction ToDoubleFunction IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
1个 参数; 返回类型相同 | UnaryOperator apply() | UnaryOperator IntUnaryOperator LongUnaryOperator DoubleUnaryOperator |
2 个参数类型相同; 返回类型相同 | Comparator compare() | Comparator |
2 个参数; 返回布尔型 | Predicate test() | Predicate BiPredicate IntPredicate LongPredicate DoublePredicate |
参数基本类型; 返回基本类型 | 类型To类型Function applyAs类型() | IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
2 个参数类型不同 | Bi操作 (不同方法名) | BiFunction BiConsumer BiPredicate ToIntBiFunction ToLongBiFunction ToDoubleBiFunction |
3.1 Supplier
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestSupplier {
static Supplier<String> s = () -> "我是Supplier型接口";
static BooleanSupplier bs = () -> true;
static IntSupplier is = () -> 100;
static LongSupplier ls = () -> 100L;
static DoubleSupplier ds = () -> 100.00;
public static void main(String[] args) {
// Supplier类型
System.out.println(s.get()); // 我是Supplier型接口
System.out.println(bs.getAsBoolean()); // true
System.out.println(is.getAsInt()); // 100
System.out.println(ls.getAsLong()); // 100
System.out.println(ds.getAsDouble()); // 100.0
}
}
3.2 Consumer
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestConsumer {
static Consumer<String> c = str -> System.out.println("consumer " + str);
static IntConsumer ic = i -> System.out.println(i * 100);
static LongConsumer lc = l -> System.out.println(l * 100);
static DoubleConsumer dc = d -> System.out.println(d * 100);
public static void main(String[] args) {
// Consumer类型
c.accept("kns"); // consumer kns
ic.accept(2); // 200
lc.accept(3L); // 300
dc.accept(4D); // 400.0
}
}
3.3 BiConsumer
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestBiConsumer {
static BiConsumer<String, Integer> bc = (str, i) -> {
Integer result;
try {
result = Integer.parseInt(str);
} catch (Exception e) {
result = 0;
}
System.out.println(result + i);
};
public static void main(String[] args) {
bc.accept("100", 200); // 300
}
}
3.4 ObjConsumer
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestObjConsumer {
static ObjIntConsumer<String> oic = (o, i) -> System.out.println(o + i);
static ObjLongConsumer<String> olc = (o, l) -> System.out.println(o + l);
static ObjDoubleConsumer<String> odc = (o, d) -> System.out.println(o + d);
public static void main(String[] args) {
oic.accept("obj", 100); // obj100
olc.accept("obj", 100L); // obj100
odc.accept("obj", 100.0); // obj100.0
}
}
3.5 Function
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestFunction {
static Function<String, String> func = s -> s;
// 将数字转换为字符串
static IntFunction<String> iFunc = String::valueOf;
static LongFunction<String> lFunc = String::valueOf;
static DoubleFunction<String> dFunc = String::valueOf;
// 将字段串转换为数字类型
static ToIntFunction<String> tif = Integer::parseInt;
static ToLongFunction<String> tlf = Long::parseLong;
static ToDoubleFunction<String> tdf = Double::parseDouble;
// 将类型转换另一个类型
static IntToLongFunction ilf = i -> i;
static IntToDoubleFunction idf = i -> i;
static LongToIntFunction lif = l -> (int)l;
static LongToDoubleFunction ldf = l -> l;
static DoubleToIntFunction dif = d -> (int)d;
static DoubleToLongFunction dlf = d -> (long)d;
public static void main(String[] args) {
System.out.println(func.apply("abc"));
System.out.println(iFunc.apply(100));
System.out.println(lFunc.apply(200L));
System.out.println(dFunc.apply(300.0));
System.out.println(tif.applyAsInt("111"));
System.out.println(tlf.applyAsLong("222"));
System.out.println(tdf.applyAsDouble("333"));
System.out.println(ilf.applyAsLong(100));
System.out.println(idf.applyAsDouble(100));
System.out.println(lif.applyAsInt(200L));
System.out.println(ldf.applyAsDouble(200L));
System.out.println(dif.applyAsInt(300.0));
System.out.println(dlf.applyAsLong(300.0));
}
}
3.6 UnaryOperator
/**
* 继承自Function
* 入参返参相同
* @author kenewstar
* @date 2022/01/23
*/
public class TestUnaryOperator {
static UnaryOperator<String> uo = s -> s;
static IntUnaryOperator iuo = i -> i * 2;
static LongUnaryOperator luo = l -> l * 2;
static DoubleUnaryOperator duo = d -> d * 2;
public static void main(String[] args) {
System.out.println(uo.apply("I am kns"));
System.out.println(iuo.applyAsInt(100));
System.out.println(luo.applyAsLong(200));
System.out.println(duo.applyAsDouble(300.0));
}
}
3.7 BinaryOperator
/**
* @author kenewstar
* @date 2022/01/23
*/
public class TestBinaryOperator {
static BinaryOperator<String> bo = (s1, s2) -> s1 + s2;
static IntBinaryOperator ibo = Integer::sum;
static LongBinaryOperator lbo = Long::sum;
static DoubleBinaryOperator dbo = Double::sum;
public static void main(String[] args) {
System.out.println(bo.apply("I am", " kns"));
System.out.println(ibo.applyAsInt(12, 23));
System.out.println(lbo.applyAsLong(24, 46));
System.out.println(dbo.applyAsDouble(36, 69));
}
}
3.8 Comparator
/**
* 比较器类型
* @author kenewstar
* @date 2022/01/23
*/
public class TestComparator {
static Comparator<Integer> comparator = Integer::compareTo;
public static void main(String[] args) {
System.out.println(comparator.compare(10, 12));
}
}
3.9 Predicate
/**
* 断言型函数式接口 返回boolean值
* @author kenewstar
* @date 2022/01/23
*/
public class TestPredicate {
static Predicate<String> p = Objects::nonNull;
static BiPredicate<String, String> eq = Objects::equals;
static IntPredicate ip = i -> i > 100;
static LongPredicate lp = l -> l > 200;
static DoublePredicate dp = d -> d > 300;
public static void main(String[] args) {
// 是否不为Null对象
System.out.println(p.test("abc"));
System.out.println(eq.test("abc", "abc"));
System.out.println(ip.test(200));
System.out.println(lp.test(200));
System.out.println(dp.test(200));
}
}