Lambda表达式入门

1 篇文章 0 订阅

Lambda表达式入门

Java 8之前,大家应该有创建匿名内部类的体验,代码有点繁琐。

Lambda表达式支持将代码块,作为方法参数,允许使用更简洁的代码,来创建只有一个抽象方法的接口的实例。Java 8里面,只有一个抽象方法的接口,叫做函数式接口,由@FunctionalInterface修饰,例如线程常用到的Runnable接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Lambda表达式构成

三部分构成

  • 形参列表。如果只有一个参数,连括号都可以省

  • 箭头 ->

  • 代码块。如果只有一条语句,可省略花括号。Lambda表达式需要返回值,如果只有一条return语句,则可以省略

interface Eatable {
    void taste();
}

interface Flyable {
    void fly(String weather);
}

interface Addable {
    int add(int a, int b);
}

//
public class LambdaTest {

    public void eat(Eatable e) {
        System.out.println(e);
        e.taste();
    }

    public void drive(Flyable f) {
           System.out.println("Driving....");
           f.fly("===Sunny Day===");
       }

    public void sum(Addable add) {
        System.out.println("5 plus 3 is " + add.add(5, 3));
    }


    public static void main(String[] args) {
        LambdaTest lt = new LambdaTest();
        //需要Eatable接口的实例,代码块只有一条,省略{}
        lt.eat(() -> System.out.println("The cake is delicious"));
        //需要Flyable接口实例,只有一个形参,省略()
        lt.drive(weather -> {
            System.out.println("Today's weather is " + weather);
            System.out.println("Steady!");
        });
        //需要Addable接口实例,接口只有一条语句,省略{},省略return
        lt.sum((a, b) -> a + b);

    }

}
运行结果
com.LambdaTest$$Lambda$1/834600351@548c4f57
The cake is delicious
Driving....
Today's weather is ===Sunny Day===
Steady!
5 plus 3 is 8

Lambda表达式的类型

从上面的代码可以看到,传入的参数是各种类型的接口的实例,而实际传入的是lambda表达式,还编译通过,运行正常。

说明lambda表达式会被当成”任意类型”,准确来说,是目标类型(target type),就是表达式需要创建的那个接口实例的类型

lambda表达式的目标类型必须是函数式接口。Java 8里,函数式接口可以包含多个default方法,static方法,但只能有一个abstract方法

Lambda表达式,实现的,是匿名方法,因此,只能实现特定函数式接口中的唯一方法。意味着,有2点限制

  • 目标类型必须函数式接口

  • 只能为函数式接口创建对象

保证目标类型是函数式接口的方法:

  • lambda表达式赋值给函数式接口类型的变量

  • lambda表达式作为函数式接口类型的参数传给某个方法

  • 强制类型转换

//这代码不会编译通过,Object不是函数式接口
Object object = () -> {
    for(int i=0;i<10;i++){
        System.out.println(i);
    }
};

//强制类型转换为函数式接口
Object object = (Runnable)() -> {
    for(int i=0;i<10;i++){
        System.out.println(i);
    }
};

java.util.function

Java 8 里,预定义了大量函数式接口,典型含如下5种类型。用过Scala或Spark的人,应该会觉得似曾相识。个人感觉,Java 8里的这些操作跟Scala是很像的

XXXFunction

  • 这类接口通常含有R apply(T t);抽象方法方法
  • 方法对参数T处理,转换,最后返回一个新的值R
  • 具体的处理,转换逻辑,由lambda表达式的代码块决定
  • 通常用于对指定数据的转换处理
  • XXXOperator继承Function

XXXConsumer

  • 通常有void accept(T t);抽象方法
  • 与XXXFunction接口类似,只是没有返回值
  • 负责对参数处理
  • Unlike most other functional interfaces, {@code Consumer} is expected to operate via side-effects

XXPredicate

  • 通常有boolean test(T t);抽象方法
  • 用来对参数做某种判断,具体逻辑由lambda代码块决定,最终返回一个boolean值
  • 通常用于判断参数是否满足条件,用作筛选

Predicate verb(formal):to state that sth is true

XXXSupplier

  • 通常含有XXX getAsXXX();方法,Supplier则含有T get();方法
  • 该方法不需要参数,返回一个数据
  • 每次返回的结果,可以一样

方法引用和构造器引用

用实例来说明。首先,定义一个Converter函数式接口,目标是把字符串类型转换为Integer类型,至于怎么转,由lambda表达式具体实现

@FunctionalInterface
interface Converter {
    Integer convert(String from);
}

引用静态方法/类方法

格式
类名::方法名. 对应的的lambda, (a,b...) -> 类名.静态方法(a,b...)
public static void main(String[] args) {
    //创建Converter实例,转换方法的具体实现,是透过Integer的静态方法实现的
    Converter converter = from -> Integer.valueOf(from);
    Integer integer = converter.convert("100");
    System.out.println(integer);
    //静态方法引用
    Converter converter2 = Integer::valueOf;
    Integer val2 = converter2.convert("1000");
    System.out.println(val2);
}

引用特定对象的实例方法

格式
特定对象::实例方法名. 对应的lambda, (a,b...) -> 特定对象.实例方法(a,b...)
//找出输入字符串在实例对象中的位置
Converter instance = from -> "Specific Intance Method Reference".indexOf(from);
Integer val3 = instance.convert("I");
System.out.println(val3);

Converter instance2 = "Specific Intance Method Reference"::indexOf;
Integer val4 = instance2.convert("I");
System.out.println(val4);

引用某类的对象的实例方法

格式
类名::实例方法名. 对应的的lambda, (a,b...) -> a.实例方法(b...). 表达式中的第一个参数为方法调用者
@FunctionalInterface
interface StringTest {
    String test(String str, int begin, int end);
}

...

//接口里传3个参数,最后返回String类型
//使用了String类的对象的substring方法
StringTest st = (str,beginIndex,endIndex) -> str.substring(beginIndex, endIndex);
String result = st.test("Specific Instance of a Class Method Reference", 7, 15);
System.out.println(result);

StringTest st2 = String::substring;
String result2 = st.test("Specific Instance of a Class Method Reference", 7, 15);
System.out.println(result2);

引用构造器

格式
类名::new. 对应的的lambda, (a,b...) -> new (a,b...).
@FunctionalInterface
interface NewTest {
    JFrame frame(String title);
}

//表达式代码块只有一条语句
NewTest nt = title -> new JFrame(title);
JFrame jFrame = nt.frame("MY WINDOW");
System.out.println(jFrame);

NewTest nt2 = JFrame::new;
JFrame jFrame2 = nt.frame("MY WINDOW");
System.out.println(jFrame2);

Lambda表达式与匿名内部类

相同

  • 直接访问”effective final”的局部变量。即,访问过的局部变量(包括实例变量和类变量/静态变量),相当于局部变量被final修饰

  • 都可以直接调用接口的默认方法

不同

  • 匿名内部类可以创建任意接口的实例,不管接口有多少个抽象方法。lambda表达式只能创建函数式接口的实例

  • 匿名内部类可以创建抽象类的实例。lambda不可以。

  • 匿名内部类所实现的抽象方法的方法体,允许调用接口定义的默认方法。lambda的代码块不可以

在数组上的测试

String[] arr1 = new String[] {"Java","fkava","fkit","ios","android"};
//字符串越长,越大
Arrays.parallelSort(arr1,(o1,o2) -> o1.length() - o2.length());
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.deepToString(arr1));

int[] arr2 = new int[]{3,-4,25,16,30,18};
//两两计算
Arrays.parallelPrefix(arr2, (left, right)->left * right);
System.out.println(Arrays.toString(arr2));

long[] arr3 = new long[5];
//利用索引来计算
Arrays.parallelSetAll(arr3, generator -> generator - 5);
System.out.println(Arrays.toString(arr3));

//结果
[ios, Java, fkit, fkava, android]
[ios, Java, fkit, fkava, android]
[3, -12, -300, -4800, -144000, -2592000]
[-5, -4, -3, -2, -1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值