4

Java中如何使用lambda实现懒加载?

 8 months ago
source link: https://www.jdon.com/71450.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java中如何使用lambda实现懒加载?

当我们使用需要执行昂贵或缓慢方法的资源(例如数据库查询或 REST 调用)时,我们倾向于使用本地缓存或私有字段。

lambda 函数允许我们使用方法作为参数并推迟方法的执行或完全省略它。

在本教程中,我们将展示如何使用 lambda 函数延迟初始化字段,注意不是延迟方法执行,而是实现字段懒加载。

但是在实现懒加载之前,先了解一些lambda 函数推迟方法的执行。

Lambda推迟方法执行
在Java中,Lambda表达式可以用于推迟方法的执行,特别是在与函数式接口(Functional Interface)一起使用时。

函数式接口是只包含一个抽象方法的接口。通过Lambda表达式,你可以将这个抽象方法的执行推迟到Lambda表达式被调用的时候。下面是一个简单的示例:

@FunctionalInterface
interface MyFunctionalInterface {
    void myMethod();
}

public class LambdaDelayExecution {
    public static void main(String[] args) {
        // 使用Lambda表达式实现函数式接口
        MyFunctionalInterface myFunction = () -> {
            System.out.println("方法执行!");
        };

// 调用Lambda表达式中的方法
        // 这里才会真正执行Lambda表达式中的myMethod方法
        myFunction.myMethod();
    }
}
在上述示例中,MyFunctionalInterface是一个函数式接口,它定义了一个名为myMethod的抽象方法。通过Lambda表达式,我们可以实现这个接口并在Lambda表达式中定义myMethod的具体实现。在main方法中,当调用myFunction.myMethod()时,Lambda表达式中的方法才会真正执行。

需要注意的是,Lambda表达式常常与Java中的Streams API一起使用,这样可以更方便地实现推迟执行的效果。例如:

import java.util.Arrays;

public class LambdaDelayExecution {
    public static void main(String[] args) {
        // 使用Streams API中的forEach方法,延迟执行
        Arrays.asList("One", "Two", "Three")
              .stream()
              .map(s -> {
                  System.out.println("Mapping: " + s);
                  return s.toUpperCase();
              })
              .forEach(s -> System.out.println("ForEach: " + s));
    }
}
在这个例子中,map方法中的Lambda表达式的执行被延迟到forEach方法调用时。这种延迟执行的方式可以有效地进行数据处理和转换。

lambda 函数懒加载
Lambda函数只提供推迟方法执行,不提供字段懒初始化方式,但是我们可以转个弯使用。

import java.util.Optional;
import java.util.function.Supplier;
import java.util.concurrent.atomic.AtomicReference;

public class LazyLoadingExample {
     private final AtomicReference<String> data = new AtomicReference<>();

private Supplier<String> lazyField = () -> createSomeType();


    private String createSomeType() {
        System.out.println("只执行一次哦");
       return "Created";

}

public String getLazyField() {
        if (data.get() == null) {
            synchronized (data) {
                if (data.get() == null) {
                    data.set(lazyField.get());
                }
            }
        }
        return data.get();
    }

public static void main(String[] args) {
        LazyLoadingExample example = new LazyLoadingExample();
         //在第一次访问时,实例被创建
        String lazyField = example.getLazyField();
        //在后续访问时,不会再次创建实例
        String lazyFieldAgain = example.getLazyField();

}
}

上面代码输出:

只执行一次哦

getLazyField()中使用了Double-Checked Locking(双重检查锁定)保证多线程安全性。

  • 我们需要想象多个线程同时调用getData()方法。线程确实会阻塞,并且执行将按顺序进行,直到data.get()调用不为 null。一旦数据字段初始化完成,多个线程就可以并发访问它。
  • 有人可能会认为getData()方法中的双 null 检查是多余的,但事实并非如此。事实上,外部 null 检查确保当data.get()不为 null 时,线程不会阻塞在同步块上。

结论
在本文中,我们展示了使用 lambda 函数延迟初始化字段的不同方法。通过使用这种方法,我们可以避免多次执行昂贵的调用并推迟它们。我们的示例可以用作本地缓存或Project Lombok的lazy-getter 的替代方案。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK