Bootstrap

JDK8新特性-lambda表达式

一.概念

Lambda表达式是特殊的匿名内部类,语法更简洁

Lambda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递

二.语法

基本语法

<函数式接口><变量名>=(参数1,参数2....)->{方法体}

(parameters) -> expression 或 (parameters) ->{ statements; }

函数式接口:接口中只有一个抽象方法

1.paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。

2.->:可理解为“被用于”的意思

3.方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。

4.如果想用lambda表达式必须是函数式接口

// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

三.无参无返回值lambda表达式

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}
//无返回值两个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a,int b);
}


public class TestDemo {
    public static void main(String[] args) {
        NoParameterNoReturn n = ()->{
            System.out.println("无参数无返回值");
        };
        n.test();

        OneParameterNoReturn o = (a)-> {
            System.out.println("无返回值一个参数"+a);
        };
        o.test(666);
        MoreParameterNoReturn m = (int a,int b)->{
            System.out.println("无返回值两个参数"+a+" "+b);
        };
        m.test(666,999);
    }
}

四.有返回值函数接口

//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}
//有返回值多个参数
@FunctionalInterface
interface MoreParameterReturn {
    int test(int a,int b);
}


public class TestDemo {
    public static void main(String[] args) {
        NoParameterReturn n = ()->{
            return 666;
        };
        int ret1 = n.test();
        System.out.println(ret1);
        System.out.println("================");
        OneParameterReturn o = (int a)->{
            return a;
        };
        int ret2 = o.test(999);
        System.out.println(ret2);
        System.out.println("================");
        MoreParameterReturn m = (int a,int b)-> {
            return a+b;
        };
        int ret3 = m.test(10,90);
        System.out.println(ret3);
    }
}

五.语法精简

  1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略
  3. 如果方法体当中只有一句代码,那么大括号可以省略
  4. 如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字
    	public static void main(String[] args) {
            MoreParameterNoReturn moreParameterNoReturn = (a, b)->{
                System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b);
            };
            moreParameterNoReturn.test(20,30);
            OneParameterNoReturn oneParameterNoReturn = a ->{
                System.out.println("无参数一个返回值,小括号可以省略:"+ a);
            };
            oneParameterNoReturn.test(10);
            NoParameterNoReturn noParameterNoReturn = ()->System.out.println("无参数无返回值,方法体中只有 一行代码");
            noParameterNoReturn.test();
            //方法体中只有一条语句,且是return语句
            NoParameterReturn noParameterReturn = ()-> 40;
            int ret = noParameterReturn.test();
            System.out.println(ret);
        }

    六.函数式接

    要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法

    1.如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口,可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上

    2.如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。

    3.Lambda表达式只能简化函数式接口的匿名内部类的写法

    定义方式:

    @FunctionalInterface
    interface NoParameterNoReturn {
    	void test();
    	default void test2() {
    		System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    	}
    }

    1.常见的函数式接口

        一、匿名内部类,new函数式接口
public class Demo01 {
    //匿名内部类的方式,new接口,里面重写接口中的方法
    Function<String,Object> function = new Function<String,Object>() {
        @Override
        public Object apply(String str) {
            return str;
        }
    };
}
二、Lambda表达式替换函数式接口
public class Demo01 {
    public static void main(String[] args) {
        //Lambda表达式简化匿名内部类
        Function<String,Object> function = (str)->{return str;};
        
        System.out.println(function.apply("张三"));
    }
}
三.消费型接口 Consumer<T>
  @Test
    public void test1(){
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println(aDouble);
            }
        });
 
        happyTime(500,money -> System.out.println(money));
    }
 
    public void happyTime(double money, Consumer<Double> con){
        con.accept(money);
    }
四. 函数型接口 Function<T, R>
  @Test
    public void test3(){
        String str1 = integerToString(new Integer(3), new Function<Integer, String>() {
            @Override
            public String apply(Integer integer) {
                return integer.toString();
            }
        });
        System.out.println(str1);
 
        String str2 = integerToString(new Integer(4), integer -> integer.toString());
        System.out.println(str2);
    }
    public String integerToString(Integer integer, Function<Integer,String> fun){
        return fun.apply(integer);
    }
五.断定型接口 Predicate<T> 
  @Test
    public void test4(){
        List<String> list= Arrays.asList("北京","南京","上海");
 
        List<String> filterStrs=filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(filterStrs);
        
 
        List<String> filterStrs1=filterString(list,s -> s.contains("京"));
        System.out.println(filterStrs1);
    }
 
    //根据给定的规则,过滤集合中的字符串,此规则由Predicate的方法决定
    public List<String> filterString(List<String> list, Predicate<String> pre){
        ArrayList<String> filterList = new ArrayList<>();
 
        for(String s : list){
            if(pre.test(s)){
                filterList.add(s);
            }
        }
        return filterList;
    }

七.方法引用

1.语法

1.静态方法引用(Static Method Reference):使用类名和双冒号(::)来引用一个静态方法。例如,Math::max表示引用Math类中的静态max方法

类名::静态方法名称

2.实例方法引用(Instance Method Reference):使用实例对象和双冒号(::)来引用一个非静态的实例方法。例如,String::length表示引用String对象的length方法。

实例对象::方法名称

3.构造方法引用(Constructor Method Reference):使用类名和关键字new来引用一个构造方法。例如,ArrayList::new表示引用ArrayList类的构造方法。

类名::new

4.引用特定类型的任意对象的实例方法(Reference to an Instance Method of a Particular Object):使用特定对象的实例和双冒号(::)来引用该对象的实例方法。

例如,myObject::methodName表示引用myObject对象的methodName方法。

特定类实例对象::方法名称

2.案例

import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

class Vehicle {
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    // 静态方法
    public static void printName(String name) {
        System.out.println(name);
    }

    // 实例方法
    public void printPowerValue(Long powerValue) {
        System.out.println(powerValue);
    }

    @Override
    public String toString() {
        return "Vehicle{" +
                "type='" + type + '\'' +
                '}';
    }
}

public class MethodReferenceExample {

    public static void main(String[] args) {
        // 静态方法引用: 类名::静态方法
        List<String> names = Arrays.asList("擎天柱", "霸天虎", "威震天");
        names.forEach(Vehicle::printName);
        System.out.println();

        // 实例方法引用: 对象名::实例方法
        Vehicle example = new Vehicle();
        List<Long> messages = Arrays.asList(50000L, 120000L);
        messages.forEach(example::printPowerValue);
        System.out.println();

        // 构造方法引用: 类名::new
        Supplier<Vehicle> vehicleSupplier = Vehicle::new;
        // 创建对象
        Vehicle car = vehicleSupplier.get();
        car.setType("Car");
        Vehicle bike = vehicleSupplier.get();
        bike.setType("Bike");
        // 打印对象
        System.out.println(car);
        System.out.println(bike);
        System.out.println();

        // 引用特定类型的任意对象的实例方法: 对象名::实例方法
        String prefix = "Prefix:";
        List<String> suffixes = Arrays.asList("A", "B", "C");
        suffixes.stream()
                .map(prefix::concat) // 使用String的concat方法
                .forEach(System.out::println); // 使用System.out的println方法
    }
}

3.注意事项

1.方法引用只能用于函数式接口:方法引用只能用于那些只有一个抽象方法的接口,也就是函数接口。确保在使用方法引用时,目标类型是一个函数接口,否则编译将会失败。

2.参数匹配:方法引用的参数必须与目标方法的参数列表兼容。这意味着参数数量和类型要一致。如果目标方法具有多个参数,可以通过上下文推断或者函数式接口的泛型来进行类型匹配。

3.方法引用可能引发空指针异常:在使用实例方法引用时,请确保对象引用不是null。否则,在调用实例方法时会引发空指针异常。因此,在进行实例方法引用操作前,请先对对象进行必要的空值检查。

4.正确使用方法引用,增加代码可读性:正确使用方法引用可以使代码更加简洁、清晰和易于理解。然而,过度使用方法引用可能会导致代码难以阅读和维护。请在合适的情况下使用方法引用,并在需要更明确的表达时使用传统的Lambda表达式(或匿名类)来代替。

5.选择最适合的方法引用方式:熟悉各种方法引用的不同形式,包括静态方法引用、实例方法引用、构造方法引用和引用特定类型的任意对象的实例方法。了解这些不同的使用形式,可以根据具体的编码需求选择最适合的方法引用方式。

6.方法引用与Lambda表达式的比较:在某些情况下,方法引用可以代替Lambda表达式来提高代码可读性。然而,并非所有情况都适合使用方法引用。有时,使用Lambda表达式可能更清晰明了。

;