专栏首页石奈子的Java之路Java8之深入理解Lambda 顶

Java8之深入理解Lambda 顶

lambda表达式实战

从例子引出lambda

传递Runnable创建Thread

  • java8之前
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                // do something
            }
        });
  • java 8 之后
new Thread(()->{});

上边的例子比较简单,但是有两个疑问。什么是Lambda表达式?怎么使用lambda表达式?

什么是Lambda表达式?

从上述例子入手,首先我们知道Lambda一般代表的是一个匿名对象;其次我们点击“->”,IDE会帮助我们进入到符合Lambda规范的函数接口。我们来观察下这个符合规范的类的变化。

// 省略注释
package java.lang;
public interface Runnable {
   public abstract void run();
}
// 省略注释
package java.lang;
@FunctionalInterface
public interface Runnable {
   public abstract void run();
}

我们发现java8后Runnable接口新增了一个注解@FunctionalInterface。下边我们一起来看下这个注解是什么。

FunctionalInterface

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 *
 * Conceptually, a functional interface has exactly one abstract
 * method.  Since {@linkplain java.lang.reflect.Method#isDefault()
 * default methods} have an implementation, they are not abstract.  If
 * an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * <em>not</em> count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 *
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor references.
 *
 * <p>If a type is annotated with this annotation type, compilers are
 * required to generate an error message unless:
 *
 * <ul>
 * <li> The type is an interface type and not an annotation type, enum, or class.
 * <li> The annotated type satisfies the requirements of a functional interface.
 * </ul>
 *
 * <p>However, the compiler will treat any interface meeting the
 * definition of a functional interface as a functional interface
 * regardless of whether or not a {@code FunctionalInterface}
 * annotation is present on the interface declaration.
 *
 * @jls 4.3.2. The Class Object
 * @jls 9.8 Functional Interfaces
 * @jls 9.4.3 Interface Method Body
 * @since 1.8
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
  • 上边文档的核心意思是:@FunctionInterface注解是为了表明这个类是一个函数式接口。
  • 函数式接口有这样的特点:只有一个抽象方法。java8提供了default方法,以及超类Object中的方法(toString,Equals),这些方法不计算抽象方法数量的统计中。
  • 使用上:函数式接口可以配合lambda表达式方法引用构造引用使用
  • 如果类上标记了这个注解,编译器会在编译期进行检查
  • 最后,即使我们没有标注这个注解,编译器也会将它看待成一个函数式接口

好了,从上边我们知道了lambda的特点,接下来我们来聊下怎么使用?

如何使用Lambda

首先,我们去官网查阅Java8新特性,找到Lambda表达式的说明。我们从这个文档的**“Syntax of Lambda Expressions”**部分入手,大概可以得到如下的结论。

Lambda的组成

Lambda主要由下边几部分组成;参数列表,连接符,主体。

  • 参数列表
    • 圆括号内部,参数以“,”分割开来。如(String a,Object b)。
    • 此外,参数的类型和括号,有些时候是可以省略
  • 箭头记号
    • 通过“->”这种特殊符号形式,连接前后。
  • 主体
    • 可以由单个表达式,或者语句块组成。
    • 单个表达式,如"System.out.println("xxx")"
    • 语句块
      • 示例1

      { System.out.println("xxx"); }

      • 示例2

      { // do something return some result return 100; }

Lambda的完整用法示例

无返回值的lambda的用例

目的,将具体业务实现交给调用者处理。

  • 定义一个无返回值,符合FunctionInterface规范的接口对象
interface Print<String>{
    void printName(String string);
}
使用示例1

我这里的业务逻辑是根据输入参数,执行日志打印操作。实际业务场景下,可能对应的是发送邮件或者MQ这样的具体操作。

public class LambdaDemo {
    public static void main(String[] args) {
        PrintSomeThing(name->System.out.println(name),"Hello baigt");
    }
    public static void PrintSomeThing(Print<String> str,String name) {
        str.printName(name);
    }
}
使用示例1 的延伸使用
  • 定义 一个使用类
class Doctor{
    String name;
    String interest;
    public Doctor(String name, String interest) {
        this.name = name;
        this.interest = interest;
    }
    public void printName(Print<String> str) {
            str.printName(name);
    }
}
  • 具体使用
 Doctor doctor=new Doctor("baigt","java and javascript");
 doctor.printName(name->System.out.println(name));
有返回值的lambda的用例

目的,将具体业务实现交给调用者处理,并将结果返回。

  • 定义一个有返回值,符合FunctionInterface规范的接口对象
interface GetSomething<String>{
    String getThing();
}
  • 定义一个使用者
class Doctor{
    String name;
    String interest;
    public Doctor(String name, String interest) {
        this.name = name;
        this.interest = interest;
    }

    public String getInterest(GetSomething<String> get) {
        return get.getThing()+","+name;
    }
}
  • 使用示例

我这里的业务逻辑是根据输入参数(隐式interest),计算出一个结果返回出来,并对这个结果执行打印操作。

 Doctor doctor=new Doctor("baigt","java and javascript");
 System.out.println(doctor.getInterest(() -> "Hi"));

到此处,我们已经大概明白lambda表达式的基本用法。但是还会有两个疑问?

  • 上边例子我们自定义了几个函数式接口,那么还有其他常用的函数式接口?
  • 函数式接口不仅可以通过lambda表达式使用,还可以通过方法引用和构造引用来使用。那么这种引用又是怎么回事?

常用函数接口

我们选中@FunctionInterface注解类,通过Ide的Find Usages功能,会发现在java.util.function包下java8新增了很多类。这里挑几个基础的(其他的基本是功能上的增强或变种)来说。大致上有这么几种。

  • Consumer
  • Supplier
  • Predicate
  • Function

下边会做一个简单的说明和使用。可能不会细致的去讲每一个Api。旨在让大家快速熟悉使用java8 lambda。

Consumer

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

首先此接口只有一个抽象方法accept,该方法接收一个入参,不返回结果

定义使用类

    public static void doConsumer(Consumer consumer,String input) {
        consumer.accept(input);
    }
  • 使用示例1

接收 “something input”输入,并执行打印操作

 Consumer consumer = input -> System.out.println(input);
 doConsumer(consumer,"something input");
  • 使用示例2

将两个Consumer操作串连起来,andThen的后执行。

Consumer consumer = input -> System.out.println(input);
doConsumer(consumer.andThen(input2->{
            System.out.println("input2");
        }),"something input");

Supplier

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

首先此接口只有一个抽象方法get,该方法不接收参数,返回一个T类型的结果

定义使用类

    public static <T> T doSupplier(Supplier<T> supplier) {
        return supplier.get();
    }
  • 使用示例1

不传入参数,生成一个指定类型为String或Integer的对象

 System.out.println(doSupplier(() -> "baigt"));
 System.out.println(doSupplier(() -> {return Integer.valueOf("10");}));

Predicate

import java.util.Objects;

/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
}    

首先此接口只有一个抽象方法test,该方法接受一个T类型的对象,返回一个boolean类型的结果

定义使用类

    public static boolean doPredicate(Predicate<String> predicate,String string) {
        return predicate.test(string);
    }
  • 使用示例1

根据条件,判断输入对象是否符合过滤规则。

System.out.println(doPredicate(input -> input.length() > 5, "12345"));
System.out.println(doPredicate(((Predicate<String>) (input -> input.length() > 5))
        .and(input -> input.equalsIgnoreCase("12345")), "12345"));

Function

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

首先此接口只有一个抽象方法apply,该方法接收一个T类型对象,返回一个R类型的结果。

定义使用类

    public static Integer doFunction(Function<String,Integer> function,String input) {
        return function.apply(input);
    }
  • 使用示例1

接收一个String类型的入参,返回Integer类型的结果。示例中没做具体异常判断。

System.out.println(doFunction(input -> input.length(), "baigt"));
// 上述结果为 5
 System.out.println(doFunction(((Function<String, Integer>) (input -> input.length())).compose(input -> String.valueOf(input.length() * 3)), "baigt"));
// 上述结果为 2
 System.out.println(doFunction(((Function<String, Integer>) (input -> {
     System.out.println("notcompose:"+input);
     return Integer.valueOf(input)+1;
 })).compose(input -> {
     System.out.println("compose:"+input);
     return String.valueOf(Integer.valueOf(input)*3);
 }), "22"));
 // 上述结果为 67

compose是先执行的部分,上述例子中,是根据输入参数进行进一步的加工,再作为输入参数传递给具体调用者。

引用

前边提到了方法引用和构造引用两种,其实构造引用是一种特殊方法引用。具体参照官方文档说明中“Kinds of Method References”部分。

种类

用例

类名::静态方法

String::valueOf

实例对象::实例方法

doctor1::getInterest

类名::实例方法

String::toUpperCase

类名::new (构造引用)

String::new

静态引用

  • 使用类
    public static String doStaticReference(Function<Integer,String> function, Integer input) {
        return function.apply(input);
    }
  • 示例
doStaticReference(String::valueOf,123456);

实例对象引用实例方法

  • 使用类
class Doctor{
    String name;
    String interest;
    public Doctor(String name, String interest) {
        this.name = name;
        this.interest = interest;
    }
    public String getStringInstance(){
        return new String(name);
    }
 }   
  • 示例
Doctor doctor1=new Doctor("baigt007","java");
        Supplier<String> instance = doctor1::getInterest;

类引用实例方法

  • 使用类
    public static String doMethodReference(Function<String,String> function, String input) {
        return function.apply(input);
    }
  • 示例
doMethodReference(String::toUpperCase,"baigt");O

构造引用

  • 示例
Supplier<String> stringInstance = String::new;

结语

上述是个人心得,不对之处,欢迎指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入理解EnableAutoConfiguration原理

    源码分析@EnableAutoConfiguration在SpringBoot中的加载和实例化过程

    石奈子
  • 原 荐 SpringBoot 2.0 系列0

    石奈子
  • 原 SpringBoot 2.0 系列00

    石奈子
  • 使用预测的随机梯度上升学习优化环境(CS LG)

    在这项工作中,我们将直接策略搜索算法概括为一种称为“直接环境搜索”的算法,该算法具有(预测的随机)梯度上升(DESGA)。 后者可用于共同学习强化学习(RL)环...

    小童
  • flask 初始化DB指令函数(flask 97)

    from flask_script import Manager app = Flask(name) basedir = os.path.abspath(o...

    用户5760343
  • Load URL With Extra HTTP Headers Using WebView in Android

    As we know, Webview will add the referer into the reqeust if we click a hyperlin...

    技术小黑屋
  • GET请求实践--测试框架视频讲解

    讲完json对象的操作,今天开始正式进入正题——接口测试。这里的接口指的是HTTP接口测试,主要的请求方法是GET和POST,下面开始讲GET请求的测试实践。

    八音弦
  • 检测阿根廷恰加斯病潜在高流行地区(Computers and Society)

    本文提出了一种具有高空间分集性的恰加斯病(ChD)潜在患病率地图。它的目的是检测大查科生态区域化(恰加斯病的高度地方化)以外的地区,其特征是与恰加斯病高度相关和...

    李欣颖6837176
  • hdu---(5038)Grade(胡搞)

    Grade Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K (J...

    Gxjun
  • Multi-touch (MT) Protocol 小结

    A类协议在每发送完一个接触数据包后会调用 input_mt_sync() 声明 一次数据的结束; input_mt_sync() 会发出一个 SYN_MT_RE...

    233333

扫码关注云+社区

领取腾讯云代金券