Java研学-Lambda表达式

一 Lambda表达式 – 箭头函数

1 含义

  JDK8首次将函数式编程引入到Java代码中;这是一种新型的方法参数传递的方式;直接将获取参数的步骤传递给需要该参数的方法中–Lambda表达式

2 特点

  1 简化代码

  2 多核友好

  3 面向对象思想不足

public class Play {
    public static void main(String[] args) {
        // 实现Runnable的优势在于他避免了单继承的局限性,但需要通过Thread构造器将实现接口的对象传给Thread
        // 多线程输出 匿名内部类实现run方法,通过Thread类调用start开启
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程操作");
            }
        }).start();
        System.out.println("等价于");
        new Thread(()-> System.out.println("新多线程操作")).start();
    }
}

3 例子-自定义水果类,以普通方式与Lambda分别实现

自定义水果类

// 注解需导入lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fruit {
    private Long id;        // 编号
    private String name;    // 水果名
    private Integer price;  // 单价
}

普通方法

public class FruitTest {
    // 创建水果集合
    private static List<Fruit> fruits=new ArrayList<>();
    // 水果只需存储一次,进入直接初始化
    static {
        fruits.add(new Fruit(1L,"猕猴桃",6));
        fruits.add(new Fruit(2L,"苹果",5));
        fruits.add(new Fruit(3L,"苹果梨",4));
        fruits.add(new Fruit(4L,"橙子",7));
        fruits.add(new Fruit(5L,"草莓",6));
        fruits.add(new Fruit(6L,"山竹",11));
    }

    public static void main(String[] args) {
        // 查询所有6块钱的水果
        findByPrice();
        // 查询含苹果名字的水果
        findByName();
    }

    private static void findByPrice(){
        List<Fruit> prices=new ArrayList<>();
        for (Fruit f:fruits) {
            if(f.getPrice()==6){
                prices.add(f);
            }
        }
        System.out.println(prices);
    }

    private static void findByName(){
        List<Fruit> names=new ArrayList<>();
        for (Fruit f:fruits) {
            if(f.getName().contains("苹果")){
                names.add(f);
            }
        }
        System.out.println(names);
    }
}

Lambda

// 自定义接口
@FunctionalInterface
public interface IFruit {
    // 接口中定义统一操作规则 根据指定商品信息判断是否满足需求
    boolean test(Fruit f);
}

// 使用
public class FruitTest {
    // 创建水果集合
    private static List<Fruit> fruits=new ArrayList<>();
    // 水果只需存储一次,进入直接初始化
    static {
        fruits.add(new Fruit(1L,"猕猴桃",6));
        fruits.add(new Fruit(2L,"苹果",5));
        fruits.add(new Fruit(3L,"苹果梨",4));
        fruits.add(new Fruit(4L,"橙子",7));
        fruits.add(new Fruit(5L,"草莓",6));
        fruits.add(new Fruit(6L,"山竹",11));
    }
    // 通过匿名内部类改造Lambda实现接口
    public static void main(String[] args) {
        // 查询所有6块钱的水果
        findFruitByI((p)->p.getPrice()==6);
        // 查询含苹果名字的水果
        findFruitByI(p->p.getName().contains("苹果"));

    }

    private static void findFruitByI(IFruit ifr){
        List<Fruit> ff =new ArrayList<>();
        for (Fruit f:fruits) {
            if(ifr.test(f)){
                ff.add(f);
            }
        }
        System.out.println(ff);
    }
}

4 Lambda表达式格式

  Lambda的固定格式核心符号是 ->
  左侧:表示通过Lambda表达式改造方法的参数部分
  右侧:表示通过Lambda表达式进行改造的方法的方法体

5 Lambda表达式改造前提

  必须是接口,并且接口中有且仅有一个抽象方法,为保证接口中只定义一个抽象方法,JDK8提出使用@FunctionaIInterface(函数式接口)

@FunctionalInterface
public interface IFruit {
    // 接口中定义统一操作规则 根据指定商品信息判断是否满足需求
    boolean test(Fruit f);
}

6 Lambda表达式特点演示

public class FruitTest {
	......
    public static void main(String[] args) {
        // ① 没有特殊要求改造方法的参数类型可以省
        List<String> list= Arrays.asList("大黄","大白","小黑");
        // foreach类似增强for
        list.forEach(s-> System.out.println(s));
        // ② 改造方法仅有一个参数时()可以省,没有参数或有多个参数时()不能省
        new Thread(()-> System.out.println("多线程启动")).start();
        // 实例化时通过代码块为map赋值
        Map<String,String> map=new HashMap<String,String>(){
            {
                this.put("大黄","15kg");
                this.put("大白","12kg");
            }
        };
        // ③ 改造方法仅有一条代码{}与;都可以省去,若有多条代码则不能省去,一般改造方法只有一条代码
        map.forEach((k,v)->{
            System.out.println(k);
            System.out.println(v);
        });
        // ④ 改造方法仅一条代码,且为return语句时,return关键字可省去
        findFruitByI(new IFruit() {
            @Override 
            public boolean test(Fruit f) {
                return f.getPrice()==6;
            }
        });
        findFruitByI(p->p.getPrice()==6);
    }
    private static void findFruitByI(IFruit ifr){
        List<Fruit> ff =new ArrayList<>();
        for (Fruit f:fruits) {
            if(ifr.test(f)){
                ff.add(f);
            }
        }
        System.out.println(ff);
    }
}

7 常见函数式接口

函数式接口参数类型返回类型说明
Consumer< T>消费型接口Tvoid对类型为T的对象进行操作,方法:void accept(T t)
Supplier< T>供给型接口T返回类型为T的对象 方法:T get();(可做工厂)
Function< T,R>函数型接口TR对类型为T的对象进行操作,并且返回结果是R类型(任意数据类型),方法:R apply(T t);
Predicate< T>断言型接口Tboolean判断类型为T的对象是否满足条件,返回值固定为布尔类型,方法 boolean test(T t)
public class FunctionPlay {
    // 创建水果集合
    private static List<Fruit> fruits = new ArrayList<>();

    // 水果只需存储一次,进入直接初始化
    static {
        fruits.add(new Fruit(1L, "猕猴桃", 6));
        fruits.add(new Fruit(2L, "苹果", 5));
        fruits.add(new Fruit(3L, "苹果梨", 4));
        fruits.add(new Fruit(4L, "橙子", 7));
        fruits.add(new Fruit(5L, "草莓", 6));
        fruits.add(new Fruit(6L, "山竹", 11));
    }

    public static void main(String[] args) {
        buy(36, new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println("买了"+integer+"元的水果");
            }
        });
        buy(456,(consumer)-> System.out.println("买了"+consumer+"元的水果"));

        int it=getRealLength("playthis", new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });
        System.out.println(it);
        System.out.println(getRealLength("playthis",s -> s.length()));

        System.out.println(getCode(6, new Supplier<Integer>() {
            @Override
            public Integer get() {
                return (int)(Math.random()*10);
            }
        }));
        System.out.println(getCode(8,()->(int)(Math.random()*10)));

        isOk(new Predicate<Fruit>() {
            @Override
            public boolean test(Fruit fruit) {
                return fruit.getPrice()>8;
            }
        });
    }

    // 购买消费金额
    public static void buy(int moeny,Consumer<Integer> consumer){
        // 接口对象consumer调用方法accept传递参数moeny
        consumer.accept(moeny);
    }
    // 获取长度(String真实长度)
    public static int getRealLength(String let, Function<String,Integer> fun){
        return fun.apply(let);
    }
    // 返回指定位数随机码
    public static String getCode(int num, Supplier<Integer> sup){
        // 创建StringBuffered
        StringBuffer sb=new StringBuffer();
        for (int i = 0; i < num; i++) {
            sb.append(sup.get());
        }
        return sb.toString();
    }
    // 判断水果是否超过8元
    public static void isOk(Predicate<Fruit> fp){
        for (Fruit f:fruits) {
            if(fp.test(f)){
                System.out.println(f.toString());
            }
        }
    }
}

8 方法引用

① 普通调用方法的方式

  对象.方法名([参数])

  类名.方法([参数])–必须被static修饰

② 方法引用

  进行Lambda表达式改造后,传递的操作已定义完毕(Java核心类库提供或者是其他程序员定义好的方法),可使用方法引用的方式调用

③ 方法引用的前提

  引用的方法必须是定义好的,必须是Lambda表达式改后的方法

④ 格式-- ::(引用运算符,两个引号)

::左侧 – 通过什么方式引用的即 对象名或类名

::右侧 – 调用方法的名字(方法引用只能引用无参方法,故不能写())

public class PlayMethod {
    public static void main(String[] args) {
        // 对象::方法名
        fun1();
        // 类名::静态方法
        fun2();
        // 类名::方法
        fun3();
        // 类名::new
        fun4();
    }
    // 方法引用有局限性,调用定义好的方法不能灵活应用
    // list.forEach(p-> System.out.println(p));
    private static void fun1(){
        List<String> list= Arrays.asList("大黄","大白","小黑");
        list.forEach(System.out::print);
    }
    // ()->Math.random()
    private static void fun2(){
        Supplier sup=Math::random;
        // get方法获取值
        System.out.println(sup.get());
    }
    // s->s.length()
    private static void fun3(){
        Function<String,Integer> fun=String::length;
        System.out.println(fun.apply("8537矿泉水"));
    }
    // 类名::new 构造器引用
    private static void fun4(){
        /*Supplier<String> sup=new Supplier<String>() {
            @Override
            public String get() {
                return new String();
            }
        };*/
        // ()->new String();
        Supplier<String> sup=String::new;
        System.out.println(sup.get());
    }
}

9 延迟执行

又称延迟加载或懒加载

public class PlayMethod {
    public static void main(String[] args) {
        String name="大黄";
        String is="是";
        String color="黄色的";
        // 拼接 无论是否满足条件此刻都已经完成拼接存放在工具中了(性能浪费),满足条件才提供结果
        addString("ok",name+is+color);
    }
    private static void addString(String info, String s) {
        if("ok".equals(info)){
            System.out.println(s);
        }
    }
}

使用供给型接口实现延迟加载

public class PlayMethod {
    public static void main(String[] args) {
        String name="大黄";
        String is="是";
        String color="黄色的";
        // 拼接
        /*addString("no", new Supplier<String>() {
            @Override
            public String get() {
                return name+is+color;
            }
        });*/
        addString("ok",()->name+is+color);
    }
    private static void addString(String info, Supplier<String> sup) {
        if("ok".equals(info)){
        // 判断通过才拼接字符串
            System.out.println(sup.get());
        }
    }
}

10 接口的多继承

① 接口能够定义的类成员

  成员常量:public static final 数据类型 常量名=值;

  抽象方法: 返回 方法名([参数]);

  JDK8后,在接口中可定义default和static方法,能够具有方法体

② 代码演示
定义接口A,包含抽象方法fun1()的default方法fun2(),以及static方法fun3();

public interface A {
    void fun1();
    default void fun2(){
        System.out.println("Afun2");
    }
    static void fun3(){
        System.out.println("Afun3");
    }
}

定义接口B,包含default方法fun2(),以及static方法fun3();


C类分别实现AB两个接口

public class C implements A,B{
    @Override
    public void fun1() {

    }
    // 一个类实现多个接口时,若接口中default方法同名
    // 需重写default方法确认调用哪个default方法
    @Override
    public void fun2() {
        B.super.fun2();
    }
}
  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰勒疯狂展开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值