lambda表达式的概念
使用->表达我的我们叫做lambda表达式,实现了指定接口,并返回接口对象的一种写法。
public class ThreadDemo {
public static void main(String[] args) {
Object target = new Runnable() {
@Override
public void run() {
System.out.println("ok");
}
};
new Thread((Runnable) target).start();
// jdk8 lambda
Object target2 = (Runnable)() -> System.out.println("ok");
Runnable target3 = () -> System.out.println("ok");
System.out.println(target2 == target3); // false
new Thread((Runnable) target2).start();
}
}
对于使用lambda表达式的接口的要求就是接口中必须只有一个接口方法。这也是引导我们写接口的时候尽量少写接口,尽量遵守单一责任制。jdk8中引入了一个新的名称叫函数接口,使用@FunctionalInterface标注,作用同@override一样是编译器的校验作用。
默认方法,或者叫默认实现方法,我们可以通过lambda表达式得到一个接口实例然后就可以对用这个默认方法,我们可以把默认方法当做类里面的方法来理解。
@FunctionalInterface
interface Interface1 {
int doubleNum(int i);
default int add(int x, int y) {
return x + y;
}
static int sub(int x, int y) {
return x - y;
}
}
public static void main(String[] args) {
Interface1 i1 = (i) -> i * 2;
Interface1.sub(10, 3);
System.out.println(i1.add(3, 7));
System.out.println(i1.doubleNum(20));
}
jdk8中提供的函数接口
首先实现一个需求对数字进行格式,化格式化成钱的格式 即“#,###” 我们使用如下代码来实现
import java.text.DecimalFormat;
/**
* @author 杨红星
* @version 1.0.0
* @date 2018/7/29
*/
interface IMoneyFormate {
String formate(int money);
}
class MyMoney1 {
private final int money;
public MyMoney1(int money) {
this.money = money;
}
public void printMoney(IMoneyFormate moneyFormat) {
System.out.println("我的存款:" + moneyFormat.formate(this.money));
}
}
public class MoneyDemoOld {
public static void main(String[] args) {
MyMoney1 me = new MyMoney1(99999999);
me.printMoney( i -> new DecimalFormat("#,###")
.format(i));
}
}
我们查看代码可以发现其实这个接口IMoneyFormate没有多少必要,事实上我们可以认为这个接口的作用就是告诉我们参数类型返回值类型,所以可以去掉这个接口同时使用jdk8中的新特性。使用Function<Integer, String>来表明方法接受的是一个接口类型,同时指定参数和返回值类型分别是intger和string,调用apply方法。最后一个同时使用了新特性中函数接口链式操作 下面看代码:
class MyMoney {
private final int money;
public MyMoney(int money) {
this.money = money;
}
public void printMoney(Function<Integer, String> moneyFormat) {
System.out.println("我的存款:" + moneyFormat.apply(this.money));
}
}
public class MoneyDemo {
public static void main(String[] args) {
MyMoney me = new MyMoney(99999999);
Function<Integer, String> moneyFormat = i -> new DecimalFormat("#,###")
.format(i);
// 函数接口链式操作
me.printMoney(moneyFormat.andThen(s -> "人民币 " + s));
}
}
新增特性默认接口方法 默认方法可以就当做类里面的方法 不仅有默认放还可以有静态方法 看代码
@FunctionalInterface
interface Interface1 {
int doubleNum(int i);
default int add(int x, int y) {
return x + y;
}
static int sub(int x, int y) {
return x - y;
}
}
public strictfp class LambdaDemo1 {
public static void main(String[] args) {
Interface1 i1 = (i) -> i * 2;
Interface1.sub(10, 3); //7
System.out.println(i1.add(3, 7)); //10
System.out.println(i1.doubleNum(20)); //40
// 这种是最常见写法
Interface1 i2 = i -> i * 2;
Interface1 i3 = (int i) -> i * 2;
Interface1 i4 = (int i) -> {
System.out.println("-----");
return i * 2;
};
}
}
断言函数和消费者函数
public class FunctionDemo {
public static void main(String[] args) {
// 断言函数接口
IntPredicate predicate = i -> i > 0;
System.out.println(predicate.test(-9));
//
// IntConsumer
// 消费函数接口
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("输入的数据");
}
}
方法引用
Consumer<String> consumer = s -> System.out.println(s);
当你的方法的参数和返回值的类型一样我们就可以缩写 去掉s用 ::代替即
Consumer<String> consumer=System.out::println
方法引用的种类
1、静态方法的引用
使用方法类名加方法名 类名::方法名
2、非静态方法 非静态方法的实例::方法名
非静态方法也可以使用类名加方法名的形式,印为jdk会为每一方法传值时第一个参数为this 如
public void test(String s){
}
编译的时候变成
//Demo为类名
public void test(Demo this,String s){
}
所以在非静态方法中使用类名加方法名的方式为
BigFunction <类名(实例的类型),输入参数类型,输出类型>=类名::new
3、构造函数的方法引用
不含参数
function<参数类型,类名(实例的类型,返回值的类型)> 函数名称= 类::new
含参数的
function<参数类型,类名(实例的类型,返回值的类型)> 函数名称=类名::new
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
class Dog {
private String name = "哮天犬";
/**
* 默认10斤狗粮
*/
private int food = 10;
public Dog() {
}
/**
* 带参数的构造函数
*
* @param name
*/
public Dog(String name) {
this.name = name;
}
/**
* 狗叫,静态方法
*
* @param dog
*/
public static void bark(Dog dog) {
System.out.println(dog + "叫了");
}
/**
* 吃狗粮 JDK
*
* 默认会把当前实例传入到非静态方法,参数名为this,位置是第一个;
*
* @param num
* @return 还剩下多少斤
*/
public int eat(int num) {
System.out.println("吃了" + num + "斤狗粮");
this.food -= num;
return this.food;
}
@Override
public String toString() {
return this.name;
}
}
public class MethodRefrenceDemo {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(3);
// 方法引用
Consumer<String> consumer = System.out::println;
consumer.accept("接受的数据");
// 静态方法的方法引用
Consumer<Dog> consumer2 = Dog::bark;
consumer2.accept(dog);
// 非静态方法,使用对象实例的方法引用
// Function<Integer, Integer> function = dog::eat;
// UnaryOperator<Integer> function = dog::eat;
IntUnaryOperator function = dog::eat;
// dog置空,不影响下面的函数执行,因为java 参数是传值
dog = null;
System.out.println("还剩下" + function.applyAsInt(2) + "斤");
//
// // 使用类名来方法引用
// BiFunction<Dog, Integer, Integer> eatFunction = Dog::eat;
// System.out.println("还剩下" + eatFunction.apply(dog, 2) + "斤");
//
// // 构造函数的方法引用
// Supplier<Dog> supplier = Dog::new;
// System.out.println("创建了新对象:" + supplier.get());
//
// // 带参数的构造函数的方法引用
// Function<String, Dog> function2 = Dog::new;
// System.out.println("创建了新对象:" + function2.apply("旺财"));
}
private static void test(List<String> list) {
list = null;
}
}
lambda表达式的类型推断
package lambda;
@FunctionalInterface
interface IMath {
int add(int x, int y);
}
@FunctionalInterface
interface IMath2 {
int sub(int x, int y);
}
public class TypeDemo {
public static void main(String[] args) {
// 变量类型定义
IMath lambda = (x, y) -> x + y;
// 数组里
IMath[] lambdas = { (x, y) -> x + y };
// 强转
Object lambda2 = (IMath) (x, y) -> x + y;
// 通过返回类型
IMath createLambda = createLambda();
TypeDemo demo = new TypeDemo();
// 当有二义性的时候,使用强转对应的接口解决
demo.test( (IMath2)(x, y) -> x + y);
}
public void test(IMath math) {
}
public void test(IMath2 math) {
}
public static IMath createLambda() {
return (x, y) -> x + y;
}
}
lambda表达式的变量引用
插曲在jdk8之前内部类引用外部类这个类必须是final类型,在jdk8中这个final是可以省略的,但是依然是final的
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* 变量引用
*/
public class VarDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Consumer<String> consumer = s -> System.out.println(s + list);
consumer.accept("1211");
}
}
联级表达式和柯里化
联级表达式就是x->y->x+y;这种形式
完整的写出来意思就清楚了
Function<Integer,Function<Integer,Integer>>=x->y->x+y;
就是说y是一个函数然后作为x的返回函数,y又是x+y的返回函数
所谓柯里化就是多个参数变成一个参数看代码
import java.util.function.Function;
/**
* 级联表达式和柯里化
* 柯里化:把多个参数的函数转换为只有一个参数的函数
* 柯里化的目的:函数标准化
* 高阶函数:就是返回函数的函数
*/
public class CurryDemo {
public static void main(String[] args) {
// 实现了x+y的级联表达式
Function<Integer, Function<Integer, Integer>> fun = x -> y -> x
+ y;
System.out.println(fun.apply(2).apply(3));
Function<Integer, Function<Integer, Function<Integer, Integer>>> fun2 = x -> y -> z -> x
+ y + z;
System.out.println(fun2.apply(2).apply(3).apply(4));
int[] nums = { 2, 3, 4 };
Function f = fun2;
for (int i = 0; i < nums.length; i++) {
if (f instanceof Function) {
Object obj = f.apply(nums[i]);
if (obj instanceof Function) {
f = (Function) obj;
} else {
System.out.println("调用结束:结果为" + obj);
}
}
}
}
}