一、环境和软件准备
环境:window或者linux环境
下载软件:
1.JDK版本:JDK9下载路径
2.安装eclipse,当然可以使用其他编辑器
二、Lambda表达式预演
1.面向对象的Runnable接口写法
public class DemoRunnable {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("多线程执行了!");
}
};
new Thread(task).start();
}
}
2.使用函数式编程思想
异同点:
- [ 面向对象强调“一切皆对象”,如果想要做事情,必须找到对象来做]
- [函数式编程思想强调“做什么,而不是怎么做” ]
例子:
public class DemoRunnable {
public static void main(String[] args) {
new Thread(()->System.out.println("多线程执行了!")).start();
}
}
分析:这样的代码是不是更加简洁。
Runnable接口当中的run方法语义分析:
- 参数列表为空,不需要任何条件就可以执行该方法
- 没有返回值:方法不产生任何数据结果
- 方法体大括号:这个才是关键的方法内容所在
Lambda表达式:
- 前面一个小括号:不需要任何参数条件,即可直接执行
- 箭头指向后面要做的事情
- 箭头后面就好比是方法体大括号,代表具体要做的内容
三、Lambda表达式的标准格式
1.Lambda表达式必须有上下文推导:
- a.根据调用方法的参数推导得知Lambda对应的接口
- b.根据局部变量的赋值来推导得知Lambda对应的接口
2.三要素:一些参数、一个参数、一些代码
3.比如:(参数类型 参数名称) -> {一些代码}
- a. 如果参数有多个,那么使用逗号分隔,如果没有参数,则留空。
- b.箭头是固定写法
- c.大括号其实就相当于是方法体
例子
1.新建一个接口类
package com.lambda.demo;
/**
* 使用Lambda表达式的必要条件:
* 1.必须有一个接口
* 2.接口当中必须保证有且仅有一个抽象方法
* @FunctionalInterface:注释用来检测是不是函数式接口
* 1.如果是函数式接口,那么编译通过
* 2.如果不是,那么就编译失败
* @author Administrator
*
*/
@FunctionalInterface
public interface Cook {
//唯一的抽象方法
void makeFood();
}
2.之前写法要写一个接口实现类
package com.lambda.demo;
public class CookImpl implements Cook{
@Override
public void makeFood() {
System.out.println("菜煮好,吃饭啦!");
}
}
3.执行类
package com.lambda.demo;
public class DemoCook {
private static void method(Cook cook) {
cook.makeFood();
}
public static void main(String[] args) {
//调用实现类
method(new CookImpl());
//可以使用匿名内部类
method(new Cook() {
@Override
public void makeFood() {
System.out.println("菜煮好,吃饭啦!");
}
});
/**使用标准的lambda表达:根据上下文(method方法)推导,因为Cook只用一个抽象方法,
* 所以能推 导出调用的方法一定是makeFood()方法,该方法没有参数,那么使用(),括号是空的,
* {}填写实现的内容
**/
method(() -> {System.out.println("菜煮好,吃饭啦!");});
}
}
四、Lambda表达式的参数和返回值
1.新建一个Person类
package com.lambda.demo;
public class Person {
private String name;
private int age ;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
2.执行类
package com.lambda.demo;
import java.util.Arrays;
import java.util.Comparator;
public class DemoPersonSort {
public static void main(String[] args) {
Person[] arry = {
new Person("古天",36),new Person("张家",35),
new Person("周星",56),new Person("任达",45)
};
System.out.println(Arrays.toString(arry));
Arrays.sort(arry,new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//o1大就是正数,o2大就是负数,一样大就是0
return o1.getAge()-o2.getAge();
}
});
System.out.println(Arrays.toString(arry));
//使用Lambda表达式 ,有返回值使用return
Arrays.sort(arry,(Person p1,Person p2) -> {return p2.getAge()-p1.getAge();});
System.out.println(Arrays.toString(arry));
}
}
五、Lambda表达式的省略规则:
1、参数类型可以省略,但是只能省略所有参数的类型,或者干脆都不省略,不能只写个别参数类型
2、如果参数有且仅有一个,那么小括号可以省略。
3、如果大括号之内的语句有且仅有一个,那么无论有没有返回值,return、大括号、分号都可以省略
package com.lambda.demo1;
public interface Calculator {
public int add(int a,int b);
}
package com.lambda.demo1;
//1.使用Lambda表达式的必要条件:
// 必须有一个接口; 接口当中必须保证有且仅有一个抽象方法
public class DemoCalculatorLambda {
public static void main(String[] args) {
method((int a,int b) -> {return a+b;});
}
private static void method(Calculator calc){
int add = calc.add(100, 200);
System.out.println(add);
}
}
public class DemoCalculatorLambdaFormat {
public static void main(String[] args) {
// method((a,b) -> {return a+b;});
method((e,y) -> e+y);
}
private static void method(Calculator calc){
int add = calc.add(100, 200);
System.out.println(add);
}
}
六、接口的组成更新
1.接口组成部分:
- a.常量
- b.抽象方法
- c.默认方法(Java 8)
- d.静态方法(Java 8)
- e.私有方法(Java 9)
默认方法例子
package com.jiahui.interfaceDefaultDemo;
/**
* 接口的实现类当中必须对接口所有抽象方法都要覆盖重写,除非实现类是一个抽象类。
* 接口升级:本来是2个抽象方法,现在需要变成3个抽象方法,但是别的实现类中已经有再使用抽象方法了。
* java7以前的版本解决方案是重新定义一个抽象类继承原本的抽象类,然后再写新增的抽象方法;设计模式当中的对扩展开放对修改关闭
* java8开始,接口当中允许定义defaul默认方法。
* 常量的修饰符:public static final(都可以省略)
* 默认方法的修饰符:public default(public可以省略,default不能省略)
*
* 默认方法可以有方法体实现;默认方法也可以被覆盖重写
* @author
*
*/
public interface MyInterface {
void method1();
void method2();
//新定义一个方法
public default void methoNew() {
System.out.println("接口的默认方法执行!");
}
}
接口实现类
package com.jiahui.interfaceDefaultDemo;
public class MyInterfaceImplA implements MyInterface{
@Override
public void method1() {
}
@Override
public void method2() {
}
//默认方法也可以被实现,也可以不用实现
@Override
public void methoNew() {
System.out.println("A接口的默认方法执行");
}
}
package com.jiahui.interfaceDefaultDemo;
public class MyInterfaceImplB implements MyInterface{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
执行
package com.jiahui.interfaceDefaultDemo;
public class MyInterfaceMain {
public static void main(String[] args) {
MyInterface objA = new MyInterfaceImplA();
objA.methoNew();
MyInterface objB = new MyInterfaceImplB();
objB.methoNew();
}
}
静态方法例子
package com.jiahui.interfaceStaticDemo;
/**
* 从java8开始,接口当中允许定义静态方法:
* 静态方法的修饰符:public static (public可以省略,static不可以省略)
* @author jiahui
*
*/
public interface Animal {
void eat();
public static Animal getAnimal() {
return new Cat();
}
}
接口实现类
package com.jiahui.interfaceStaticDemo;
public class Cat implements Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
package com.jiahui.interfaceStaticDemo;
public class Dog implements Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
执行
package com.jiahui.interfaceStaticDemo;
/**
* 左边是接口类型,说明我并不关心到底是猫还是狗,只要是动物
* @author jiahui
*
*/
public class AnimalMain {
public static void main(String[] args) {
/*Animal animal = new Dog();
animal.eat();*/
//新的调用方式
Animal animal = Animal.getAnimal();
animal.eat();
}
}
私有方法例子
package com.jiahui.interfacePrivateDemo;
/**
* 从java9开始支持在接口当中定义私有方法:
* 私有方法可以是:1.成员私有方法;2.静态私有方法
* @author Administrator
*/
public interface MyInterfacePrivate {
default void method() {
System.out.println("这个是method方法");
makeFood();
}
default void method2() {
System.out.println("这个是method2方法");
makeFood();
}
private void makeFood() {
System.out.println("点火");
System.out.println("上锅");
System.out.println("放油");
System.out.println("炒菜");
System.out.println("装盘");
}
static void methodA() {
System.out.println("静态成员方法1");
makeCookie();
}
static void methodS() {
System.out.println("静态成员方法2");
makeCookie();
}
private static void makeCookie() {
System.out.println("洗米");
System.out.println("开电");
System.out.println("洗碗");
System.out.println("吃饭");
}
}
执行
package com.jiahui.interfacePrivateDemo;
public class InterfaceMain {
public static void main(String[] args) {
MyInterfacePrivate.methodA();
MyInterfacePrivate.methodS();
}
}
七、通过方法引用改进代码
1.从java8开始 ,引入一个全新的运算符,方法引用符(两个冒号“::”)所在的表达是一个方法引用,方法引用和Lambda本质是完全一样的,目的就是为了简化Lambda表达式的写法
例子
package com.jiahui.interfaceRef;
/**
* @author Administrator
*/
@FunctionalInterface
public interface Printer {
void print(String str);
}
执行
package com.jiahui.interfaceRef;
public class Demo01Printer {
public static void main(String[] args) {
method((String str)->{System.out.println(str);});
method(str -> System.out.println(str));
//使用方法引用;和lambda本质是一样的
method(System.out::println);
}
private static void method(Printer printer) {
printer.print("hello me");
}
}
成员方法引用例子
package com.jiahui.interfaceRef.helper;
public class Assistant {
void work() {
System.out.println("努力工作!!!");
}
}
package com.jiahui.interfaceRef.helper;
@FunctionalInterface
public interface WorkHelper {
void help();
}
执行
package com.jiahui.interfaceRef.helper;
public class DemoMethodRef {
public static void main(String[] args) {
Assistant assistant = new Assistant();
assistant.work();
method(()->System.out.println("努力工作!!!"));
//成员方法引用
method(assistant::work);
}
private static void method(WorkHelper helper) {
helper.help();
}
}
静态方法引用例子
package com.jiahui.interfaceRef.staticDemo;
@FunctionalInterface
public interface Calculator {
int getAbs(int num);
}
执行
package com.jiahui.interfaceRef.staticDemo;
public class DemoMethodStaticRef {
public static void main(String[] args) {
method((t)->{return Math.abs(t);});
//如果lambda表达式需要的事情,正好就是一个类当中的静态方法内容。
//那么可以使用静态方法引用:(类名::静态方法名)
method(Math::abs);
}
private static void method(Calculator calculator) {
int abs = calculator.getAbs(-200);
System.out.println("结果是:"+abs);
}
}
父类方法引用例子
package com.jiahui.interfaceRef.superDemo;
public class Human {
public void sayHello() {
System.out.println("Hello!");
}
}
执行
package com.jiahui.interfaceRef.superDemo;
public class Man extends Human{
@Override
public void sayHello() {
method(()->System.out.println("Hello!"));
method(()->super.sayHello());
//如果lambda表达式要做的事情,正好就是父类当中的方法内容,
//那么可以通过super关键字来引用父类当中的方法,格式:(super::父类方法名称)
method(super::sayHello);
}
private void method(Greetable greetable) {
greetable.greet();
System.out.println("I am a man");
}
}
本类方法引用例子
package com.jiahui.interfaceRef.thisDemo;
@FunctionalInterface
public interface Richable {
void buyHouse();
}
执行
package com.jiahui.interfaceRef.thisDemo;
/**
* @author Administrator
*/
public class Husband {
public void marry(Richable richable) {
richable.buyHouse();
}
public void beHappy() {
marry(()->System.out.println("买一个大房子!"));
marry(()->this.buy());
/**如果lambda表达式要做的事情,正好就是本类当中的方法内容,
* 那么可以使用方法引用,借助this关键字,格式如(this::本类方法名称)**/
marry(this::buy);
}
public void buy() {
System.out.println("买一个大房子!");
}
}
数组构造器引用例子
package com.jiahui.interfaceRef.construct;
@FunctionalInterface
public interface ArrayBuilder {
int[] build(int length);
}
执行
package com.jiahui.interfaceRef.construct;
public class ArrayBuilderDemoArray {
private static void method(ArrayBuilder arrayBuilder) {
int[] build = arrayBuilder.build(10);
System.out.println("数组长度为:"+build.length);
}
public static void main(String[] args) {
method(t->new int[t]);
/**数组构造器引用,格式(原始类型[]::new)**/
method(int[]::new);
}
}
类构造器引用例子
package com.jiahui.interfaceRef.construct;
public class Person {
private String name;
public Person() {
}
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.jiahui.interfaceRef.construct;
@FunctionalInterface
public interface PersonBuilder {
Person build(String name);
}
执行
package com.jiahui.interfaceRef.construct;
public class DemoConstructor {
public static void method(PersonBuilder builder) {
Person person = builder.build("能省");
System.out.println("名字叫作:"+person.getName());
}
public static void main(String[] args) {
method(t->new Person(t));
/**如果lambda表达式要做的事情就是构造方法的内容,那么可以使用构造器引用,格式:(类名称::new)**/
method(Person::new);
}
}
七、JDK常用内置的函数式接口
1.从java8开始 ,jdk内置很多函数式接口,下面介绍几个常用到的
Supplier例子
package com.jiahui.interfaceRef.supplierDemo;
import java.util.function.Supplier;
/**
* 为了更好地支持函数式编程,所以jdk给我们内置了很多常用的函数式接口,
* 一般都是放在java.util.function包中
* java.util.function.supplier<T>接口作用:“向外提供”一个数据
*
* lambda表达式如果要使用外部局部变量,局部变量必须是有效final的。
* @author Administrator
*
*/
public class DemoSupplier {
private static void method(Supplier<String> supplier) {
String str = supplier.get();
System.out.println("字符串:"+str);
}
public static void main(String[] args) {
String dString = "Hello";
// dString += "abc";如果是这样子就会报错,编译器通不过,因为必须是final的
method(()->dString);
}
}
Consumer例子
package com.jiahui.interfaceRef.cunsumerDemo;
import java.util.function.Consumer;
/***
* @author Administrator
*/
public class DemoConsumer {
//参数当做消费者
public static void method(Consumer<String> consumer) {
//这个方法就是内保存数据,相当于接收数据
consumer.accept("ddsjsdkdfjdkfjdkf");
}
private static void method02(Consumer<String> one,Consumer<String> two) {
//先做one操作,然后再做two操作,将二者拼装到一起:函数拼装
one.andThen(two).accept("Hello");
}
public static void main(String[] args) {
method(t->{String upperCase = t.toUpperCase();
System.out.println(upperCase);
});
method02(one -> System.out.println(one.toLowerCase()), two->System.out.println(two.toUpperCase()));
}
}
package com.jiahui.interfaceRef.cunsumerDemo;
import java.util.function.Consumer;
public class DemoConsumer2 {
private static void Method(Consumer<String> one,Consumer<String> two,String[] array) {
for (int i = 0; i < array.length; i++) {
one.andThen(two).accept(array[i]);
}
}
public static void main(String[] args) {
String[] array = {"迪x热巴,女","古t娜扎,女","吉k隽逸,女","赵e雷,男"};
Method(one->{System.out.println(one.split(",")[0]);}, two->{System.out.println(two.split(",")[1]);}, array);
}
}
Predicate例子
package com.jiahui.interfaceRef.predicateDemo;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* 为了更好地支持函数式编程,所以jdk给我们内置了很多常用的函数式接口,
* 一般都是放在java.util.function包中
* java.util.function.Predicate<T>接口作用:对指定类型的对象进行操作,
* 得到一个boolean值结果
*
* lambda表达式如果要使用外部局部变量,局部变量必须是有效final的。
* @author Administrator
*
*/
public class DemoPredicate {
private static void method(Predicate<String> predicate) {
boolean veryLong = predicate.test("Hello");
System.out.println("字符串太长:"+veryLong);
}
public static void main(String[] args) {
method((s)->s.length()>4);
}
}
Function例子
package com.jiahui.interfaceRef.functionDemo;
import java.util.function.Function;
/**
* java.util.function.Function<T,R>
* 第一个泛型代表:函数参数类型
* 第二个泛型代表:函数的返回值类型
* @author Administrator
* 抽象方法:apply(T param)
*/
public class DemoFunction {
private static void method(Function<String, Integer> function) {
Integer apply = function.apply("100");
System.out.println(apply);
}
public static void main(String[] args) {
method(Integer::parseInt);
method(t->Integer.parseInt(t));
}
}
package com.jiahui.interfaceRef.functionDemo;
import java.util.function.Function;
/**
* Function接口当中默认方法:
* andThen 先做自己在做其他
* compose 先做其他再做自己
* @author Administrator
*
*/
public class DemoAndThen {
private static void method(Function<String, String> one,Function<String, Integer> two){
Integer age = one.andThen(two).apply("女神,23");
age +=1;
System.out.println("年龄"+age);
}
private static void methodCompose(Function<String, String> one,Function<String, Integer> two){
Integer age = two.compose(one).apply("女神,23");
age +=1;
System.out.println("年龄"+age);
}
public static void main(String[] args) {
method(one -> one.split(",")[1], Integer::parseInt);
methodCompose(one -> one.split(",")[1], Integer::parseInt);
}
}
八、JDK9使用函数式接口
package com.jiahui.interfaceStaticDemo2;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* java8当中接口中可以定义的静态方法,这个特性在java9当中得以广泛应用。
* 集合接口中的工厂静态方法:of
* @author Administrator
*
*/
public class DemoCollection {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("广东省");
list.add("北京市");
list.add("天津市");
list.add("海南省");
System.out.println(list);
List<String> list2 = new ArrayList<String>() {
{
add("贵州省");
add("云南省");
add("安徽省");
add("广西");
}
};
System.out.println(list2);
List<String> list3 = List.of("湖北省", "湖南省", "四川省", "青海省", "河北省", "甘肃省");
System.out.println(list3);
//里面的元素不能重复,重复就会报错
Set<String> set = Set.of("洪七公","黄药师","欧阳锋","段智兴");
System.out.println(set);
Map<String, Object> map = Map.of("猫","百");
System.out.println(map);
Map<String, Object> map2 = Map.of("女孩","漂亮","男孩子","帅气");
System.out.println(map2);
}
}