函數式接口是爲Java8中的lambda而設計的,lambda表達式的方法體就是函數接口的實現。
一、什麼是函數式接口?
函數式接口是隻包含一個方法的抽象接口。比如:Java標準庫中的Java.lang.Runnable,java.util.Callable就是典型的函數式接口。
二、如何使用函數是接口?
在Java8中通過@FunctionalInterface進行註解,將一個接口的標註爲函數式接口,該接口只包含一個抽象方法。 @Functionallnterface註解不是必須的,只要接口只包含一個抽象方法,虛擬機會自動判斷該接口爲函數式接口。 一般建議在接口上使用@Functionallnterface註解進行聲明,以免他人錯誤的往接口中添加新的方法。當使用該註解後,如果在該接口中定義了第二個抽象方法的話,編譯器會報錯。
函數式接口爲我們提供了四大內置核心函數式接口:
package Lambda;
import org.junit.Test;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/*
*Java8 內置的四大核心函數式接口
*
*Consumer<T> : 消費型接口
* void accept(T t);
*Supplier<T> : 供給型接口
* T get();
*Funcation<T,R> : 函數型接口
* R apply(T t);
*Predicate<T> : 斷言型接口
* Boolean test(T t);
*
* */
public class FuncationCoreInterface {
// Consumer<T> : 消費型接口
@Test
public void fun1(){
happy(100000,(m) -> System.out.println("今天消費:" +m+"元。"));
}
public void happy(double money, Consumer<Double>consumer){
consumer.accept(money);
}
// Supplier<T> : 供給型接口
@Test
public void fun2(){
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
for (Integer num:numList) {
System.out.println(num);
}
}
// 需求:產生指定個數的整數,並放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i =0;i<num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
// Funcation<T,R> : 函數型接口
@Test
public void fun3(){
String s1 = strHandler("\t\t\t\t\t hdfs", (s) -> s.trim());
System.out.println(s1);
String s2 = strHandler("common", (s) -> s.substring(2, 4));
System.out.println(s2);
}
// 需求:用於處理字符串
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
// Predicate<T> : 斷言型接口
@Test
public void fun4(){
List<String> list = Arrays.asList("hello", "lanbda", "world", "java", "ok");
List<String> list1 = filterStr(list, (s) -> s.length() <= 4);
for (String str:list1) {
System.out.println(str);
}
}
// 需求:將滿足條件的字符串,放入到集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String str:list) {
if (pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
三、什麼是Lambda表達式?
Lambda表達式是一個匿名函數。可以將它看做匿名內部類,但從虛擬機的角度來看,並不是如此,因爲Lambda表達式在編譯的時候,並不會生成xxx$1.class的匿名類,而是通過動態綁定,在運行的時候再調用,因此避免了在編譯時生成匆匆而影響jvm的加載速度。
四、怎麼使用lambda表達式?
使用Lambda表達式的時候,接口必須是函數式接口。
基本語法:(<函數式接口> <變量名> = (參數1,參數2...) -> { //方法體 })
Java8中引入了一個新的操作符“->”該操作符稱爲箭頭操作符或者Lambda操作符,箭頭操作符將Lambda表達式拆分成兩個部分:
左側:Lambda表達式的參數列表。
右側:Lambda表達式所需執行的內容,即Lambda體。
語法格式一:無參數,無返回值。() -> System.out.println("Hello Lambda");
package Lambda;
/*接口定義,無參無返回值*/
@FunctionalInterface
public interface ICompute {
public void print();
}
package Lambda;
public class testAnonymousInnerClass {
public static void main(String[] args) {
/*傳統內部類測試*/
int i = 12;
hello(new ICompute() {
@Override
public void print() {
System.out.println(i);
System.out.println("Fuck");
}
});
/*lambda測試*/
hello(()-> System.out.println("Hello lambda"));
}
/*定義使用該接口的方法*/
private static void hello(ICompute iCompute) {
iCompute.print();
}
}
語法格式二:有一個參數,並且無返回值。(x) -> System.out.println(x);
package Lambda;
@FunctionalInterface
public interface HelloInterface {
void sayHello(String message);
}
package Lambda;
public class Test {
public static void main(String[] args) {
sayHello("peter",x-> System.out.println("Hello "+x));
}
private static void sayHello(String message,HelloInterface helloInterface){
helloInterface.sayHello(message);
}
}
語法格式三:若只有一個參數,小括號可以省略不寫。x -> System.out.println(x);
語法格式四:有兩個以上的參數,有返回值,並且Lambda體中有多條語句。
Comparator<Integer> com = (x, y) -> {
System.out.println("函數式接口");
return Integer.compare(x, y);
};
語法格式五:若Lambda體重只有一條語句,return和大括號都可以省略不寫。
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
語法格式六 : Lambda 表達式的參數列表的數據類型可以省略不寫,因爲JVM編譯器通過上下文推斷出,數據類型,即“類型推 斷”
(Integer x, Integer y) -> Integer.compare(x, y);
注 : Lambda 表達式中的參數類型都是由編譯器推斷得出的。 Lambda 表達式中無需指定類型,程序依然可以編譯,這是因爲 javac 根據程序的上下文,在後臺推斷出了參數的類型。 Lambda 表達式的類型依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的 “類型推斷”
上聯 : 左右遇一括號省 下聯 : 左側推斷類型省 橫批 : 能省則省
package Lambda;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
public class LambdaParam {
@Test
public void test1(){
// 無參數,無返回值
final int num = 0; //jdk 1.7前,必須是final
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("HelloWorld" + num);
}
};
System.out.println("========我是分割線=========");
Runnable r1 = () -> System.out.println("HelloLambda");
}
// 一個參數,無返回值
@Test
public void test2(){
Consumer<String> con = (x) -> System.out.println(x);
con.accept("哈哈哈哈");
Consumer<String> con1 = x -> System.out.println(x);
con1.accept("嘖嘖嘖嘖");
}
// 有兩個參數,有返回值,Lambda體中有多條語句
@Test
public void test3(){
Comparator<Integer> com = (x,y) -> {
System.out.println("函數式接口");
return Integer.compare(x,y);
};
// 如果只有一條語句,大括號和return都可以不寫
Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
}
// Lambda表達式的參數列表可以省略不寫,因爲jvm編譯器可以自動推斷出參數類型
@Test
public void test4(){
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com1 = (Integer x,Integer y) -> Integer.compare(x,y);
}
}
Lambda表達式的應用:
需求:
1、獲取公司中員工年齡大於35歲的信息。
2、獲取當前公司中員工工資大於5000的員工信息。
//實體類
package Lambda.LambdaCase;
public class Employee {
private String name;
private Integer age;
private Double salary;
public Employee() {
}
public Employee(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
//最原始的方法
package Lambda.LambdaCase;
import org.junit.Test;
import java.util.*;
public class TestLambda {
// 需求:獲取公司中員工年齡大於35的員工信息
List<Employee> employees = Arrays.asList(
new Employee("張三",18,9999.99),
new Employee("李四",19,8888.88),
new Employee("王五",100,1.11),
new Employee("趙六",80,2.22),
new Employee("田七",20,9898.98)
);
@Test
public void test3(){
List<Employee> list = filterEmployees1(employees);
for (Employee emp:list) {
System.out.println(emp);
}
System.out.println("============我是分割線===========");
List<Employee> list1 = filterEmployees2(employees);
for (Employee emp:list1) {
System.out.println(emp);
}
}
public List<Employee> filterEmployees1(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (emp.getAge() >= 35){
emps.add(emp);
}
}
return emps;
}
// 需求:獲取當前公司中員工工資大於5000的員工信息,和上面的程序相差不多,產生大量的重複代碼
public List<Employee> filterEmployees2(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (emp.getSalary()>=5000) {
emps.add(emp);
}
}
return emps;
}
}
//優化方式一:對Employee集合按照Predicate的方式進行過濾(策略設計模式)
@Test
public void test4(){
List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
for (Employee employee:list) {
System.out.println(employee);
}
System.out.println("============我是分割線===========");
List<Employee> list1 = filterEmployee(employees, new FilterEmployeeBySalary());
for (Employee employee:list1) {
System.out.println(employee);
}
}
public List<Employee> filterEmployee(List<Employee>list,MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee emp:list) {
if (mp.test(emp)){
emps.add(emp);
}
}
return emps;
}
//需求1
package Lambda.LambdaCase;
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge()>=35;
}
}
//需求二
package Lambda.LambdaCase;
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getSalary()>=35;
}
}
//定義接口
package Lambda.LambdaCase;
public interface MyPredicate<T> {
public boolean test(T t);
}
//優化方式二:匿名內部類,省略創建的類MyPredicate
@Test
public void test5(){
List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() <= 5000;
}
});
for (Employee employee:list) {
System.out.println(employee);
}
}
//優化方式三:Lanbda表達式
@Test
public void test6(){
List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
list.forEach(System.out::println);
}
//優化方式四:Stream API
@Test
public void test7(){
employees.stream()
.filter((e) -> e.getSalary() >= 5000)
.limit(2)
.forEach(System.out::println);
System.out.println("============我是分割線===========");
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
}