欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

本文由 ImportNe 图纸加密 w - yizhe 翻译自 dzone

点击: 次  来源:劳务派遣管理系统 时间:2018-08-21

本文由 ImportNew - yizhe 翻译自 dzone。接待插手翻译小组。转载请见文末要求。

Java 中惰性求值的潜能,完全被忽视了(在语言层面上,它仅被用来实现 短路求值 )。更先进的语言,如 Scala,区分了传值挪用与传名挪用,可能引入了 lazy 这样的要害字。

尽量 Java 8 通过延迟行列的实现(java.util.stream.Stream)在惰性求值的方面有些改造,可是我们会先跳过 Stream,而把重点放在如何利用 lambda 表达式实现一个轻量级的惰性求值。

基于 lambda 的惰性求值

Scala

当我们想对 Scala 中的要领参数举办惰性求值时,我们用“传名挪用”来实现。

让我们建设一个简朴的 foo 要领,它接管一个 String 示例,然后返回这个 String:

def foo(b: String): String = b

一切都是顿时返回的,跟 Java 中的一样。假如我们想让 b 的计较延迟,可以利用传名挪用的语法,只要在 b 的范例声明上加两个标记,来看:

def foo(b: => String): String = b

假如用 javap 反编译上面生成的 *.class 文件,可以看到:

Compiled from "LazyFoo.scala"

public final class LazyFoo {

    public static java.lang.String foo(scala.Function0<java.lang.String>);

    Code:   

    0: getstatic #17 // Field LazyFoo.MODULE:LLazyFoo$;

    3: aload_0

    4: invokevirtual #19 // Method LazyFoo$.foo:(Lscala/Function0;)Ljava/lang/String;

    7: areturn

}

看起来传给这个函数的参数不再是一个 String 了,而是酿成了一个 Function0,这使得对这个表达式举办延迟计较变得大概 —— 只要我们不去挪用他,计较就不会被触发。Scala 中的惰性求值就是这么简朴。

利用 Java

此刻,假如我们需要延迟触发一个返回 T 的计较,我们可以复用上面的思路,将计较包装为一个返回 Supplier 实例的 Java Function0 :

Integer v1 = 42; // eager

Supplier<Integer> v2 = () -> 42; // lazy

假如需要耗费较长时间才气从函数中得到功效,上面这个要了解越发实用:

Integer v1 = compute(); //eager

Supplier<Integer> value = () -> compute(); // lazy

同样的,这次传入一个要领作为参数:

private static int computeLazily(Supplier<Integer> value) {
    // ...
}

假如仔细调查 Java 8 中新增的 API,你会留意到这种模式利用得出格频繁。一个最显著的例子就是 Optional#orElseGet ,Optional#orElse 的惰性求值版本。

假如不利用这种模式的话,那么 Optional 就没什么用处了… 或者吧。虽然,我们不会满意于 suppliers 。我们可以用同样的要领复用所有 functional 接口。

线程安详缓和存

不幸的是,上面这个简朴的要领是有缺陷的:每次挪用城市触发一次计较。不只多线程的挪用有这个缺陷,同一个线程持续挪用多次也有这个缺陷。不外,昆山软件公司,假如我们清楚这个缺陷,而且公道的利用这个技能,那就没什么问题。

利用缓存的惰性求值

适才已经提到,基于 lambda 表达式的要领在一些环境下是有缺陷的,因为返回值没有生存起来。为了修复这个缺陷,我们需要结构一个专用的东西,让我们叫它 Lazy :

public class Lazy<T> { ... }

这个东西需要自身同时生存 Supplier 和 返回值 T

@RequiredArgsConstructor
public class NaiveLazy<T> { 

    private final Supplier<T> supplier;

    private T value;

    public T get() {

        if (value == null) {

            value = supplier.get();

         }

        return value;

    }

}

就是这么简朴。留意上面的代码仅仅是一个观念模子,临时还不是线程安详的。

幸运的是,假如想让它变得线程安详,只需要担保差异的线程在获取返回值的时候不会触发同样的计较。这可以简朴的通过双重查抄锁定机制来实现(我们不能直接在 get() 要领上加锁,昆山软件开发,这会引入不须要的竞争):

@RequiredArgsConstructor

public class Lazy<T> {

    private final Supplier<T> supplier;

    private volatile T value;

    public T get() {
        if (value == null) {
            synchronized (this) {
                if (value == null) {
                    value = supplier.get();
                }
            }
        }
       return value;
    }
}

此刻,昆山软件开发,我们有了一个完整的 Java 惰性求值的函数化实现。由于它不是在语言的层面实现的,需要支付建设一个新工具的价钱。

更深入的接头