【Java从头开始到光头结束】No11.JDK1.8 函数式编程之Lambda

1.概述

  • 在JDK1.8时提出了函数式编程的概念,其中最据颠覆性的写代码方式无疑是Lambda:

Lambda表达式的使用需求也比较简单
1.函数式的实现首先需要一个接口
2.其次,该接口中有且只有一个抽象方法
Lambda类似于匿名内部类的模式,对此接口中的抽象方法做了一个函数式的实现,由于类似于匿名内部类的实现,所以不允许接口中有多个抽象方法,否则对不上到底是实现的哪个方法。

例如:
这里使用Lambda实现了Runable接口的run()方法

public class View01 {
    
    

    public static void main(String[] args) {
    
    
        new Thread( () -> {
    
    
            System.out.println("体验 lambdaλ");
        }).start();
    }

}

2.使用入门

  • Lambda的标准使用格式是:

() -> {}
圆括号为参数的放置位置,花括号为方法体的书写位置
这样一来就是一个简单的方法实现了

  • 使用举例:
/**
 * 抽象方法无参无返回值
 */
public interface Interface1 {
    
    

    void method();

}

/**
 * 抽象方法带参无返回值
 */
public interface Interface2 {
    
    

    void method(String str);

}

/**
 * 抽象方法带参带返回值
 */
public interface Interface3 {
    
    

    String method(String name, int age);

}

/**
 * Lambda
 */
public class Lambda_Pratice_Test1 {
    
    

    public static void main(String[] args) {
    
    

        // Lambda表达式练习(抽象方法无参无返回值)
        test1(() -> {
    
    
            System.out.println("test1");
        });

        // Lambda表达式练习(抽象方法带参无返回值)
        test2((String s) -> {
    
    
            System.out.println(s);
        }, "test2");

        // Lambda表达式练习(抽象方法带参带返回值)
        String result = test3((String name, int age) -> {
    
    
            return name + age;
        }, "chenfeilin ", 24);
        System.out.println(result);

    }

    private static void test1(Interface1 interface1) {
    
    
        interface1.method();
    }

    private static void test2(Interface2 interface2, String str) {
    
    
        interface2.method(str);
    }

    private static String test3(Interface3 interface3, String name, int age) {
    
    
        String result = interface3.method(name, age);
        return result;
    }

}

3.省略模式和注意事项

/**
 * Lambda表达式的省略模式
 * Lambda表达式的注意事项
 */
public class Lambda_Pratice_Test2 {
    
    

    public static void main(String[] args) {
    
    

        // Lambda表达式的省略模式
        /**
         * 可推导的就是可省略的
         * 参数类型可以省略,如果参数只有一个,圆括号可省略
         * 如果方法体只有一句,花括号,结尾分号可省略,return关键字也可以省略,如果有的话
         */
        test2(s -> System.out.println(s), "test2");
        test3((name, age) -> name + age, "test3", 24);

        // Lambda表达式的注意事项
        /**
         * lambda的使用需要依赖上下文环境,也就是不能随便写
         * 最常用的就是作为参数或者返回值
         * 以及作为局部变量赋值
         */
         
		// 直接这么写是不行的
		// () -> {System.out.println("this is interface1 method");}
		
        Interface1 interface1 = () -> {
    
    
            System.out.println("this is interface1 method");
        };
        interface1.method();

    }
    
    private static void test2(Interface2 interface2, String str) {
    
    
        interface2.method(str);
    }

    private static String test3(Interface3 interface3, String name, int age) {
    
    
        String result = interface3.method(name, age);
        return result;
    }

}

4.Lambda表达式和匿名内部类的区别

/**
 * Lambda 和 匿名内部类的区别
 */
public class Lambda_Test {
    
    

    public static void main(String[] args) {
    
    

        // 区别1:使用类型场景的不同
        // 匿名内部类:接口,抽象类,具体类都可以
//        useInterface(new Itface() {
    
    
//            @Override
//            public void method() {
    
    
//                System.out.println("useInterface");
//            }
//        });
//
//        useAbstract(new AbstractClass() {
    
    
//            @Override
//            public void method() {
    
    
//                System.out.println("useAbstract");
//            }
//        });
//
//        useTrueClass(new TrueClass() {
    
    
//            @Override
//            public void method() {
    
    
//                System.out.println("useTrueClass");
//            }
//        });

        // lambda:只有接口可以,抽象类和具体类不行
//        useInterface(() -> System.out.println("useInterface"));
        // Target type of a lambda conversion must be an interface
        // useAbstract(() -> System.out.println("aaa"));
        // useTrueClass(() -> System.out.println("aaa"));

        // ------------------------------------------------------------------------------

        // 区别2:使用局限性不同
        // 在接口中有多个抽象方法时,是无法使用lambda的,但是匿名内部类可以

        useInterface(new Itface() {
    
    
            @Override
            public void method() {
    
    
                System.out.println("m1");
            }

            @Override
            public void method2() {
    
    
                System.out.println("m2");
            }
        });

        // 区别3:在具体的代码实现上
        // 匿名内部类会生成一个单独class文件
        // 而lambda不会,他的匿名类文件是动态生成的

    }

    public static void useInterface(Itface itface){
    
    
        itface.method();
    }

    public static void useAbstract(AbstractClass abstractClass) {
    
    
        abstractClass.method();
    }

    public static void useTrueClass(TrueClass trueClass) {
    
    
        trueClass.method();
    }

}

public abstract class AbstractClass {
    
    

    public abstract void method();

}

public interface Itface {
    
    

    public void method();

    public void method2();

}

public class TrueClass {
    
    

    public void method(){
    
    

    }

}

5.方法引用符 ::

在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?
答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案

方法引用符由两个冒号组成,其左侧一般为类名或者对象实例,右边一般为方法名,注意是不写参数的
例如:
student::study

代码举例:

public interface PrintAble {
    
    
    void print(String s);
}

public class Test_method {
    
    

    public static void main(String[] args) {
    
    
		
		// 完整版写法
        method((String s) -> {
    
    
            System.out.println("77777777777");
        });
		
		// 省略后写法
        method(s -> System.out.println("77777777777"));

		// 方法引用
        method(System.out::println);

   }

    public static void method(PrintAble printAble) {
    
    
        printAble.print("sss");
    }

}

  • 引用类方法(静态方法)
public interface StringToInt {
    
    

    public int to(String s);

}

public class Method_test {
    
    

    public static void main(String[] args) {
    
    

        // 就是普通的静态方法调用
        ttt(Integer::parseInt);

    }

    public static void ttt(StringToInt sti) {
    
    
        int to = sti.to("666");
        System.out.println(to);
    }

}

  • 引用对象的实例方法
public interface MakeStringUp {
    
    

    public String upString(String small);

}

public class Stringhelper {
    
    

    public String Up(String s) {
    
    
        System.out.println("快快变大!");
        return s.toUpperCase();
    }

}

public class Test_class {
    
    

    public static void main(String[] args) {
    
    

        Stringhelper sh = new Stringhelper();
        StringToUp(sh::Up);

    }

    private static void StringToUp(MakeStringUp msu) {
    
    
        String hello_small = msu.upString("hello small");
        System.out.println(hello_small);
    }

}

  • 引用类的实例方法
public class MethodClass {
    
    

    private String name;
    private int age;

    public MethodClass() {
    
    
    }

    public MethodClass(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public void setField(String str) {
    
    
        String name = str.split(",")[0];
        int age = Integer.parseInt(str.split(",")[1]);
        this.name = name;
        this.age = age;
        System.out.println(name);
        System.out.println(age);
    }

    public static void staticMethod(String str) {
    
    
        System.out.println(str);
    }

    public void normalMethod(String str) {
    
    
        System.out.println(str);
    }

    @Override
    public String toString() {
    
    
        return "MethodClass{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public interface GetMethodClass {
    
    

    MethodClass getMethodClass(String name, int age);

}

public class FFYY_Test1 {
    
    

    public static void main(String[] args) {
    
    

        // 引用类的实例方法
        // 这个类的实例方法名字就很抽象,使用类名,去引用普通方法,一般我们是使用类的实例才能去调用其普通成员方法的。

        /**
         * 此时
         * 接口方法的参数为: (MethodClass methodClass, String str)
         * 我们想要引用的方法的参数为: setField(String str)
         * 我们想要引用的类的实例为 MethodClass
         *
         * 这里虽然参数匹配不上 (MethodClass methodClass, String str) != (String str)
         * 但是这里是可以使用的,并且调用的时候为 类名::普通方法名
         * 这里是没有使用类的实例来做方法调用的,而这个普通方法又必须要类的实例才能调用,因为不是静态方法
         * 什么时候可以这么写呢,在接口的参数中包含了这个类的实例的时候,这么写默认就是这个方法参数类实例调用了这个普通方法
         * 这里this的赋值也是成功了的
         */
        System.out.println("-----------------");
        MethodClass m = new MethodClass();
        System.out.println(m);
        test5(MethodClass::setField, m);
        System.out.println(m);

    }

    private static void test5(InterfaceTest interfaceTest, MethodClass methodClass) {
    
    
        interfaceTest.interfaceMethod(methodClass , "cfl,24");
    }

}

  • 引用构造器
public interface StudentBuilder {
    
    

    Student getSb(String name, int age);

}

public class Student {
    
    

    private String name = "";
    private int age = 0;

    public Student() {
    
    
    }

    public Student(String name, int age) {
    
    
        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 boolean equals(Object o) {
    
    
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
    
    
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

/**
 * 方法引用之引用构造方法
 * 其实也是引用普通类的方法,不过是构造方法,用了new关键字而已,大可以理解为引用了一个带参数和返回值的普通方法
 */
public class ConstructTest {
    
    

    public static void main(String[] args) {
    
    

        // 方法引用
        getStudent(Student::new);

    }

    private static void getStudent(StudentBuilder studentBuilder) {
    
    
        System.out.println(studentBuilder.getSb("陈菲林", 777));
    }

}

猜你喜欢

转载自blog.csdn.net/cjl836735455/article/details/108697167