欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java8-lamdba

程序员文章站 2024-03-14 17:44:04
...

目标

基础概念

引入两个概念: 函数式接口,接口中的默认方法

  • 函数式接口:如果一个接口中,只包含一个抽象方法,就可以认为是一个函数式接口。其中可以使用注解

    @FunctionalInterface 标识这个接口是一个函数式接口,当然不使用这个注解标识也是可以的。

    /**
     * 函数式接口,只包含一个抽象方法
     */
    @FunctionalInterface
    public interface MyPrint<T> {
        String output(T str);
    }
    
  • 默认方法

    默认方法用于扩展接口中的方法,jdk8 之后,为了在接口中引入其他的功能,需要在接口中提供额外的方法,不能直接在原来的接口中,添加方法,这样会导致实现该接口的类,都要做修改,所以就引入了默认方法,使用 default 标识。默认方法是一个非抽象的方法,实现该接口的类,也都继承默认方法。也可以在接口中添加静态方法,用来扩展接口的功能。

    // List 接口中的 sort 就是一个默认方法
    @SuppressWarnings({"unchecked", "rawtypes"})
        default void  sort(Comparator<? super E> c) {
            Object[] a = this.toArray();
            Arrays.sort(a, (Comparator) c);
            ListIterator<E> i = this.listIterator();
            for (Object e : a) {
                i.next();
                i.set((E) e);
            }
        }
     
      // Comparator 接口的中的 静态方法
        public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
            return Collections.reverseOrder();
        }
    

lamdba 表达式

语法:(param1,param2) -> { return xxx }

首先,函数式接口,才可以用lamdba 表达式,来表示。函数式接口中的默认方法或者静态方法用来扩展,其他的功能。

示例:

函数式接口:

/**
* 函数式接口,只包含一个抽象方法
*/
@FunctionalInterface
public interface MyPrint<T> {
 
    String output(T str);
 
    default void outputinfo(){
        System.out.println("info");
    }
}

lamdba 表达式演示:

@Test
    public void fun2() {
        String strw = "aaa";
 
        // 使用匿名内部类
         System.out.println(new MyPrint<String>() {
            @Override
            public String output(String str) {
                return str+"1";
            }
        }.output(strw));
 
       // 使用lamdba 表达式
        MyPrint<String> myPrint = (str) -> str+"2";
        System.out.println(myPrint.output(strw));
    }

从上面演示效果,lamdba 表达式,相当于把匿名内部类中需要实现的方法实现了。那如果接口中,有两个需要被实现的方法,就不能使用lamdba 表达式,因为lamdba表达式不知道你要实现那个方法,所以只能是 函数式接口,才能用lamdba 表达式表示。

再演示一些其他例子:

排序:

对于lamdba 表达式,参数类型,return,或者花括号,在有时,都可以省略,看如下代码:

List<String> strs = Arrays.asList("c", "a", "d");
// 之前的实现方式
Collections.sort(strs, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});
 
// lamdba 实现,将匿名内部类代码改为 lamdba 表达式
Collections.sort(strs, (String o1, String o2) -> {
    return o2.compareTo(o1);
});
// 只有一句,省略 return  和 花括号
Collections.sort(strs, (String o1, String o2) -> o2.compareTo(o1));
// 可以省略参数类型,会自动判断
Collections.sort(strs, (o1, o2) -> o2.compareTo(o1));
 
// ---- 上面的lamdba 表达式,idea 会提示 黄色标识,因为还可以更加简单
 
// 自然顺序的比较
Collections.sort(strs, Comparator.naturalOrder());
// 自然顺序相反的比较
Collections.sort(strs, Comparator.reverseOrder());
 

线程创建

// 将原来  new Runnable 的代码,变成了 lamdba 表达式  
@Test
    public void fun6() {
        new Thread(
                () -> System.out.println(Thread.currentThread().getName())
        ,"thread100").start();
    }

双冒号 :: 关键字

双冒号:: 关键字,用于引用方法和构造函数

方法引用是与lambda表达式相关的一个重要特性。它提供了一种不执行方法的方法。为此,方法引用需要由兼容的函数接口组成的目标类型上下文。

oracle官方介绍:

Method References
You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it’s often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

方法引用

使用lambda表达式创建匿名方法。但是,有时lambda表达式只调用现有方法。在这些情况下,按名称引用现有方法通常更清楚。方法引用使您能够做到这一点;对于已经有名称的方法,它们是紧凑、易于读取的lambda表达式。

// TODO 对于方法引用,还不是很理解,但是在语义上,其实能看懂做了什么事情,有了新理解,再补充

具体示例:参考 Java8新特性2:方法引用–深入理解双冒号::的使用

在代码中的体现,更突显在java8 stream 中的应用

实现一个小例子:根据身份证号码,将性别自动设置到对象的性别字段中

通用的身份证号码设置性别方法

// 两个参数一个是 idcard ,一个是   Consumer  接口
// 简单介绍一下 Consumer 是一个函数式接口,接受一个参数,但是不返回结果。 执行的方法 accept
private void setSexInfo(String idcard, Consumer<String> user) {
        int genderByIdCard = IdcardUtil.getGenderByIdCard(idcard); // 根据身份证,获取性别(参见 hutool 这个类库)
        if (genderByIdCard == 1) {  // 男
            user.accept("M");
        }
        user.accept("F");  // 女
    }
 

用户对象:

public class Person{
    String sex;
   public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}

测试:

    @Test
    public void fun09(){
        Person person = new Person();
        // 使用匿名函数
        setSexInfo("410327188510154456", new Consumer<String>() {
            @Override
            public void accept(String s) {
                person.setSex(s);
            }
        });
        // 使用 lamdba 表达式
        setSexInfo("410327188510154456", s -> myComparator.setSex(s));
 
        // 使用 :: 双冒号
        setSexInfo("410327188510154456", person::setSex);
    }

对于 Consumer 接口中执行的流程,可以看做是,当 user.accept(“M”); 执行时,M 会作为参数,调用对接口的实现,就是 person.setSex(s); 的逻辑

在idea 中,会给出对应的优化建议,点代码左边提示的 黄色选项 。上述代码,就可以从匿名函数到lamdba表达式到双冒号的形式的优化

java8-lamdba

Lambda的范围

参见: java8简明教程文档

对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。 你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。

一些函数式接口的简单介绍

具体代码示例:参考 java8 实战 3.4.1 使用函数接口 这一章 或者 java8 简明教程中的介绍

Predicate

接受一个输入参数,返回一个boolean 的结果。在操作stream中,filter方法中接受的参数就是 Predicate 接口

    boolean test(T t);

示例:

/**
     * Predicate 断言,一个布尔类型的函数
     * 可以用于 集合类,filter 的过滤等等
     */
    @Test
    public void fun() {
        // Predicate是一个布尔类型的函数
        Predicate<String> predicate = (s) -> s.length() > 0;
        boolean str1 = predicate.test("hello world");
        boolean str2 = predicate.test("");
 
        System.out.println(str1);
        System.out.println(str2);
 
        // 短路与 &&
        boolean test = predicate.and((s) -> s.equals("hello")).test("aaa");
        System.out.println(test);
        // 逻辑非
        boolean hello_world = predicate.negate().test("hello world");
        System.out.println(hello_world);
        // 逻辑或
        boolean aaa = predicate.or(predicate).test("aaa");
        System.out.println(aaa);
        // 判断两个对象是否相等
        boolean test2 = Predicate.isEqual("a2").test("a3");
        System.out.println(test2);
        boolean test3 = Predicate.isEqual("a3").test("a3");
        System.out.println(test3);
        boolean test4 = Predicate.isEqual(null).test("a3");
        System.out.println(test4);
 
        Predicate<String> ceshi1 = String::isEmpty;
        boolean test1 = ceshi1.test("");
        System.out.println(test1);
    }

Function

接收一个参数,返回一个结果。可以理解为数学公式 y = f(x),传入参数x,得到结果y. 在集合stream中,map方法接收的参数就是 Function

    R apply(T t);

示例:

/**
     * 表示接受一个参数,并返回一个结果
     * 可以理解为  y = f(x)
     * 接受x 参数,输出y,那么 f(x) 这个函数,我们自己定义就可以了
     */
    @Test
    public void fun02() {
 
        // 定义 f(x) 函数
        Function<Integer, Integer> function = (x) -> x + 1;
        // 获取结果
        Integer result = function.apply(5);
        System.out.println(result);
 
        // 定义 f(x) 函数
        Function<Integer, Integer> function1 = (x) -> x * 5;
 
        // compose 代表先执行 compose 传入的逻辑,再执行apply 的逻辑
        // 6
        Integer apply = function.compose(function1).apply(1);
        System.out.println(apply);
 
        // andThen 代表先执行当前的逻辑,再执行,andthen 传入的逻辑
        Integer apply1 = function.andThen(function1).apply(1);
        System.out.println(apply1);
 
        // ((1+1)+1)*5*5  建造者模式
        Integer apply2 = function.andThen(function1).andThen(function1).compose(function).apply(1);
        System.out.println(apply2);
 
        Function<String, Integer> toInteger = Integer::valueOf;
        Function<String, String> backToString = toInteger.andThen(String
                ::valueOf);
        backToString.apply("123"); // "123"
    }

Supplier

不接收参数,返回一个结果。可以理解为 实体字段的get 方法

    T get();

示例:

    /**
     * 接口产生一个给定类型的结果
     */
    @Test
    public void fun03() {
        Supplier<String> str = String::new;
        String s = str.get();
        Supplier<Person> str1 = Person::new;
        // 在执行get 方法时,才会拿到person 对象 ,spring beanfactory  在执行 getbean 的时候,才会创建该对象
        // 延迟加载的功能
        Person person = str1.get();
    }

Consumer

接收一个参数,但是不返回结果。可以理解为 实体字段的set 方法。在集合forEach 时,传入的参数就是 Consumer 接口

void accept(T t);

示例:

    /**
     * 接受一个参数输入且没有任何返回值的操作
     * 在 集合的 foreach 中 就需要填这个接口
     */
    @Test
    public void fun04() {
        Consumer<Person> personConsumer = (t) -> System.out.println("第一打印" + t.toString());
        Consumer<Person> personConsumer2 = (t) -> System.out.println("第二打印" + t.toString());
        // 执行
        personConsumer.accept(new Person());
        // 现在执行 accpect ,再执行 addthen 添加的
        personConsumer.andThen(personConsumer2).accept(new Person());
        // foreach 代码中,就调用改的 action.accept(t);
        Arrays.asList("1", "2").forEach((x) -> {
            System.out.println(x);
        });
    }

ToIntFunction

接收一个参数,返回一个int 类型的结果,跟Function 类似,这里限定了返回的结果类型是 int

    int applyAsInt(T var1);

java8-lamdba

相关标签: java8 lamdba

上一篇: Java异常详解

下一篇: Tensorflow入门

推荐阅读