我知道Java在这种情况下有智能/懒惰评估:
public boolean isTrue() {
boolean a = false;
boolean b = true;
return b || (a && b); // (a && b) is not evaluated since b is true
}
但是怎么样:
public boolean isTrue() {
boolean a = isATrue();
boolean b = isBTrue();
return b || a;
}
即使isATrue()
返回true,也会调用isBTrue()
吗?
答案 0 :(得分:28)
嗯,就语言而言 - 是的,这两个函数都被调用。
如果你重写了这个功能:
public boolean isTrue() {
return isBTrue() || isATrue();
}
如果第一个函数为真,则不会调用第二个函数。
但这是短路评估,而不是懒惰评估。懒惰的评估案例看起来像这样:
public interface LazyBoolean {
boolean eval();
}
class CostlyComparison implements LazyBoolean {
private int a, b;
public CostlyComparison(int a, int b) {
this.a=a;
this.b=b;
}
@Override
public boolean eval() {
//lots of probably not-always-necessary computation here
return a > b;
}
}
public LazyBoolean isATrue() {
return new CostlyComparison(10,30); //just an example
}
public boolean isTrue() { // so now we only pay for creation of 2 objects
LazyBoolean a = isATrue(); // but the computation is not performed;
LazyBoolean b = isBTrue(); // instead, it's encapsulated in a LazyBoolean
return b.eval() || a.eval(); // and will be evaluated on demand;
// this is the definition of lazy eval.
}
答案 1 :(得分:21)
在Java(和其他类C语言)中,这被称为short-circuit evaluation。 *
是的,在第二个例子中,总是调用isATrue
。也就是说,除非编译器/ JVM可以确定它没有可观察到的副作用,在这种情况下它可能会选择优化,但在这种情况下你不会注意到差异。
<小时/> *
我最初建议这与懒惰评估完全不同,但正如@Ingo在下面的评论中指出的那样,这是一个可疑的断言。人们可能会将Java中的短路运算符视为一种非常有限的惰性求值应用。
然而,当函数式语言强制执行惰性评估语义时,它通常是出于完全不同的原因,即防止无限(或至少是过度)递归。
子>答案 2 :(得分:17)
不,Java只对用户定义的方法进行了热切的评估。正如您所注意到的,Java的一些语言结构实现了非严格的评估。其他包括if
,?:
,while
。
我曾经学过[1],对于它对于懒惰的评价意味着什么存在一些混淆。&#34;首先,延迟评估意味着按需调用评估。 Java完全没有这样的东西。但是,有一个共同的趋势(我个人不鼓励)放松懒惰评估的定义,也包括按名称调用评估。诸如&&
之类的功能无法在按需调用与按名称调用评估之间进行区分;无论如何都会是一样的,这会掩盖这个问题。
考虑到这种放松,有人进一步反驳说声称Java有以下懒惰的评价:
interface Thunk<A> {
A value();
}
然后,您可以编写用户定义的&&
,如下所示:
boolean and(boolean p, Thunk<Boolean> q) {
return p && q();
}
然后提出声明,这表明Java具有惰性评估。但是,这是不懒惰评估,即使在松散的意义上。这里的区别在于Java类型系统不统一类型boolean
/ Boolean
和Thunk<Boolean>
。尝试将其中一个用作另一个将导致类型错误。在没有静态类型系统的情况下,代码仍然会失败。正是这种缺乏统一(静态打字与否)回答了这个问题;不,Java没有用户定义的延迟评估。当然,它可以如上所述进行模拟,但这是一种无趣的观察,它源于图灵等价。
Scala等语言具有按名称进行评估,该评估允许用户定义的and
函数,该函数等同于常规&&
函数(将终止考虑在内。请参阅[1] )。
// note the => annotation on the second argument
def and(p: Boolean, q: => Boolean) =
p && q
这允许:
def z: Boolean = z
val r: Boolean = and(false, z)
请注意,此短程序片段通过提供值终止。它还将Boolean
类型的值统一为按名称调用。因此,如果您订阅了懒惰评估的松散定义(同样,我不鼓励这样做),您可能会说Scala有懒惰的评估。我在这里提供Scala作为一个很好的对比。我建议查看Haskell的真正懒惰评估(按需调用)。
希望这有帮助!
[1] http://blog.tmorris.net/posts/a-fling-with-lazy-evaluation/
答案 3 :(得分:11)
SE8(JDK1.8)引入了Lambda expressions,可以使延迟评估更加透明。请考虑以下代码的main方法中的语句:
@FunctionalInterface
public interface Lazy<T> {
T value();
}
class Test {
private String veryLongMethod() {
//Very long computation
return "";
}
public static <T> T coalesce(T primary, Lazy<T> secondary) {
return primary != null? primary : secondary.value();
}
public static void main(String[] argv) {
String result = coalesce(argv[0], ()->veryLongMethod());
}
}
被调用函数coalesce返回第一个给定的非空值(如SQL中所示)。其调用中的第二个参数是Lambda表达式。仅当argv [0] == null时才会调用veryLongMethod()方法。在这种情况下,唯一的有效负载是在要按需延迟评估的值之前插入()->
。
答案 4 :(得分:2)
为简单起见,您可以使用java 8中的Supplier接口,如下所示:
Supplier<SomeVal> someValSupplier = () -> getSomeValLazily();
然后在代码中你可以:
if (iAmLazy)
someVal = someValSupplier.get(); // lazy getting the value
else
someVal = getSomeVal(); // non lazy getting the value
答案 5 :(得分:1)
只是想添加除了这个问题线程中提到的内容,下面是来自JVM上的Oracle文档
Java虚拟机实现可以选择解析每个 在类或接口中单独的符号引用 使用(“懒惰”或“迟”分辨率),或一次解决所有问题 当班级被验证时(“急切”或“静态”解决)。 这意味着在某些情况下,解决过程可能会继续 实现,在初始化类或接口之后。
并且作为具有延迟实现的类的示例是Stream,这来自Oracle文档在流
流是懒惰的;仅对源数据进行计算 当启动终端操作时,源元素是 只在需要时消费。
话虽如此,如果您执行以下操作,则不会显示任何内容。除非您添加启动器。
Steam.of(1, 2, 3, 4, 5).filter(number -> {
System.out.println("This is not going to be logged");
return true;
});
答案 6 :(得分:0)
如果isBTrue()返回true,则调用isATrue()吗?
是的,两者都被召唤。
答案 7 :(得分:0)
不,它没有。无论isBTrue()
的结果如何,都会调用isATrue()
。您可以通过编写这样的程序来自行验证,并在每种方法中使用print语句。
答案 8 :(得分:0)
是的isATrue()
将被调用,因为您在行boolean a = isATrue();
但如果isBTrue()
返回true
,则不会在以下情况下调用:
public boolean isTrue() {
return isBTrue() || isATrue();
}