资讯专栏INFORMATION COLUMN

Java8: Functional Interface and Lambda Expression

sunnyxd / 1909人阅读

摘要:写在前面最近在看实战感觉这本书的排版纸质内容真的都超级棒个人觉得这本书还是很值得一读本文简单或详细介绍一下的和表达式函数式接口是在才开始引入的首先看一下普通接口和函数式接口的区别普通接口指的是一些具有相同属性和行为的类的抽象函数式接口也是同

写在前面: 最近在看<>,感觉这本书的排版,纸质,内容真的都超级棒,个人觉得这本书还是很值得一读.本文简单或详细介绍一下Java8的FunctionalInterface和Lambda表达式.

What is Functional Interface?

函数式接口(Functional Interface)是在Java8才开始引入的.首先看一下Java普通接口和函数式接口的区别:

Java普通接口: 指的是一些具有相同属性和行为的类的抽象.

函数式接口: 也是同样的理解方式,它是对一些相似的方法的抽象.

How can we say those methods are similar methods?

1.如果定义的是一个泛型的函数式接口的话,比如:

@FunctionalInterface
public interface MyFunction1 {
    R calculate(T t);
}

那么所有只有一个参数并有返回值的函数都是MyFunction1的一个实例.也就是说这些方法都是相似的方法.

2.如果定义的是一个具体的函数式接口,比如:

@FunctionalInterface
public interface MyFunction2 {
    void print(Integer i);
}

那么所有参数是Integer类型的并且没有返回值的函数都是MyFunction2的一个实例.也就是说这些方法都是相似的方法.

Java的接口可以当做函数的参数,那么函数式接口自然也可以,这就叫做行为参数化.

How to define and use a funtional interface?

在Java8中已经定义了好多的函数式接口,比如:

java.lang.Runnable

java.util.Comparator

java.util.function.Predicate

java.util.function.Consumer

那么定义一个函数式接口,有哪些需要注意的地方呢?

1.添加@FunctionalInterface注解.

2.有且只有一个抽象方法.可以有多个default方法和static方法,但是这些方法都必须实现.

对于@FunctionalInterface注解,其实不加也可以,完全是可以通过编译的,但前提是这个接口需要满足上面的第二个注意点.

但又是为什么需要@FunctionalInterface?首先我们先看一个例子:

假设我们定义了一个函数式接口,但是忘记了添加了@FunctionInterface,同时这个函数式接口还没被其他地方引用,如果这个时候我们又添加了一个抽象方法,是不会报错的.因为这个接口有了多个抽象方法,所以这个接口就是一个普通的接口,而不是我们所期望的函数式接口.

这时候,如果添加了@FunctionalInterface注解的话,就会报错,提示说这个接口具有多个抽象方法.实际上,个人理解@FunctionalInterface就是手动添加约束,说明这个接口就是函数式接口,只能有一个抽象方法.

接下来编写并使用一个自定义函数式接口

// define a functional interface
@FunctionalInterface
public interface MyFunction {
    R convert(T t);
}

// use a function interface
public class Test {
    public static void main(String[] args) {
        MyFunction f = (Long l) -> String.valueOf(l);
        System.out.println(f.convert(10L));  // f: convert a Long to String 
        MyFunction g = (String s) -> Long.parseLong(s);
        System.out.println(g.convert("10")); // g: convert a String to Long
    }
}
What is Lambda expression?

咱们可以看一看<>这本书中的定义:

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式: 它没有名称,但它有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表.

匿名: 是因为它不像普通的方法有一个明确的名称

函数: 是因为Lambda函数不像方法那样属于某个特定的类,但又和方法一样,有参数列表,函数主体,返回值类型,还可能有可以抛出的异常列表

传递: Lambda表达式可以作为参数传递给方法或存储在变量中

简洁: 无须像匿名类那样写很多模板代码

个人理解,Lambda表达式完全是服务于函数式接口的,就是为了在创建一个函数式接口实例时更加的直观,简洁.比如咱们要创建一个匿名类对象时,可以看一下普通接口和函数式接口的区别:

// a common interface
public interface Common {
    void f();
    void g();
}

@FunctionalInterface
public interface MyFunction {
    R convert(T);
}

public class Test {
    public static void main(String[] args){
        // create a instance of common interface
        Common c = new Common() {
            @Override
            public void f() {}
            @Override
            public void g() {}
        };
        // create a instance of functional interface
        MyFunction f = (Long l) -> String.valueOf(l);
    }
}
Method Reference

说起方法引用之前,咱们先看一个例子

    // 1
    MyFunction f = (Long l) -> String.valueOf(l);
    // 2: 通过Lambda的上下文推断出l的数据类型
    MyFunction f = l -> String.valueOf(l);  // 
    // 3
    MyFunction f = String::valueOf;

第一种,第二种和第三种写法的作用完全是一样的,但显然第三种的写法更加简洁.形如xxx::xxx这样的表达式就是方法引用.方法引用可以被看做仅仅调用特定方法的Lambda的一种快捷写法.

How many ways to replace lambda expression with method reference?

总共有四种方法引用,下面咱们根据一个实例来说明并解释这几种方法引用.

// common class
public class FunctionInstance {
    public Integer getLength(String str) {
        return Optional.ofNullable(str).orElse("").length();  // Optional也是Java8引入的
    }
}
// test class
public class MethodReference {
    public static void main(String[] args) {
        // 1.指向静态方法的方法引用
        //Function f1 = s -> Integer.parseInt(s);
        Function f1 = Integer::parseInt;

        // 2.指向任意类型实例方法的方法引用,s为内部对象
        //Predicate f2 = s -> s.isEmpty();
        Predicate f2 = String::isEmpty;

        // 3.指向现有对象(外部对象)的实例方法的方法引用, instance为外部对象
        //Function f3 = s -> instance.getLength(s);
        FunctionInstance instance = new FunctionInstance();
        Function f3 = instance::getLength;

        // 4.构造函数引用
        //Supplier f4 = () -> new MethodReference();
        Supplier f4 = MethodReference::new;
    }
}

2和3有人可能会混淆, 反正一开始看的时候我是没区分出来.其实就看参数是为内部对象还是外部对象.内部对象就是用2,外部对象就是用3

内部对象: 指Lambda表达式中的对象,比如上面的s, 传入的参数,属于内部对象

外部对象: 指的是Lambda表达式外的对象,比如上面的instance

Summary

函数式接口: 就是一个特殊的接口,只能有一个抽象方法.所以可以将函数式接口想象成是一个函数类型(比如Predicate,Consumer或自定义的函数式接口).

Lambda表达式: 则是简化了匿名类对象的创建.正如我在上文所说的,Lambda表达式完全是服务于函数式接口的,正是因为了函数式接口的特殊性,所以才使用了Lambda表达式来创建一个函数类型实例,而不是像匿名类那样写一堆无用的模板代码.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67966.html

相关文章

  • Java8 lambda支持

    摘要:函数式编程说前,先理解下什么是函数式编程,如果你是个纯程序员,而且之前一直是没有使用过,可能还没有使用过这种编程方式。表达式可以表示闭包注意和数学传统意义上的不同。意思就是说,只要是接口类型,我们都可以传入表达式。在包下定义了各种函数接口 函数式编程 说lambdas前,先理解下什么是函数式编程,如果你是个纯Java程序员,而且之前一直是没有使用过Java8,可能还没有使用过这种编程方...

    Betta 评论0 收藏0
  • Java 8的Lambda及其在Android 开发中的应用

    摘要:由此可以看出,使用可以让你的代码在某些情况下达到何等的简洁。如果没有参数,那么前面的是必须存在的。我们知道中的,而其实就是一个只定义了一个抽象方法的。也就是说,可以访问定义它的那个方法的局部变量。而在里面,还可以访问所谓的局部变量。 上次在盆友圈发了一张照片 showImg(http://chriszou.com/images/lambda_example.png); 上面的两段代码是...

    liuhh 评论0 收藏0
  • Introducing FP in Java8

    摘要:函数副作用会给程序设计带来不必要的麻烦,引入潜在的,并降低程序的可读性。所以只能采用这种曲线救国的方式。则是把这种曲线救国拿到了台面上,并昭告天下,同时还对提供了一些语法支持。是自由变量,提供执行上下文,触发闭包执行。 背景 自从2013年放弃了Java就再也没有碰过。期间Java还发布了重大更新:引入lambda,但是那会儿我已经玩了一段时间Scala,对Java已经瞧不上眼。相比S...

    Prasanta 评论0 收藏0
  • 深入浅出 Java 8 Lambda 表达式

    摘要:在支持一类函数的语言中,表达式的类型将是函数。匿名函数的返回类型与该主体表达式一致如果表达式的主体包含一条以上语句,则表达式必须包含在花括号中形成代码块。注意,使用表达式的方法不止一种。 摘要:此篇文章主要介绍 Java8 Lambda 表达式产生的背景和用法,以及 Lambda 表达式与匿名类的不同等。本文系 OneAPM 工程师编译整理。 Java 是一流的面向对象语言,除了部分简...

    wdzgege 评论0 收藏0

发表评论

0条评论

sunnyxd

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<