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));
}
}