6

我对 JLS 15.7.415.12.4.2做了一些阅读,但它不能保证不会有任何编译器/运行时优化改变评估方法参数的顺序。

假设以下代码:

public static void main (String[] args) {
  MyObject obj = new MyObject();
  methodRelyingOnEvalOrder(obj, obj.myMethod());
}

public static Object methodRelyingOnEvalOrder(MyObject obj, Object input) {
  if (obj.myBoolean())
    return null;
  else
    return input;
}

是否保证编译器或运行时不会进行如下错误优化?这种优化可能看起来是正确的,但当评估顺序很重要时,它就是错误的。

在调用obj.myMethod更改将由返回的值的情况下,首先调用obj.myBoolean是至关重要的,obj.myMethod因为methodRelyingOnEvalOrder需要首先发生这种更改。

//*******************************
//Unwanted optimization possible:
//*******************************
public static void main (String[] args) {
  MyObject obj = new MyObject();
  methodRelyingOnEvalOrder(obj);
}

public static Object methodRelyingOnEvalOrder(MyObject obj) {
  if (obj.myBoolean())
    return null;
  else
    return obj.myMethod();
}
//*******************************

如果可能,请显示一些支持您的答案的资源或 Java 文档。

注意:请不要要求重写代码。这是我质疑评估顺序保证和编译器/运行时优化保证的特定情况。的执行obj.myMethod必须发生在main方法中。

4

2 回答 2

6

您提到的 JLS 位(15.7.4)确实保证:

每个参数表达式似乎在其右侧的任何参数表达式的任何部分之前都已被完全评估。

以及在 15.12.4.2 中:

然后使用参数值继续评估,如下所述。

“出现”部分允许进行一些优化,但它不能是可见的。在“评估然后继续”之前评估所有参数的事实表明,在方法执行之前,参数实际上已经完全评估。(或者至少,这是可见的结果。)

例如,如果您有以下代码:

int x = 10;
foo(x + 5, x + 20);

可以对其进行x + 5优化以同时评估两者x + 20:您无法检测到这种情况的发生。

但是在您给出的情况下,您能够检测到在调用obj.myMethod之后发生的调用obj.myBoolean(),因此这根本不是有效的优化。

简而言之:您可以假设一切都将以明显的方式执行。

于 2013-08-12T07:02:25.637 回答
5

除了参数评估顺序之外,调用方法时执行的步骤的概述及其顺序,在第15.12.4 节中进行了解释。方法调用的运行时评估清楚地表明所有参数评估都是在执行方法的代码之前执行的。引用:

在运行时,方法调用需要五个步骤。首先,可以计算目标参考。其次,对参数表达式求值。第三,检查要调用的方法的可访问性。第四,定位要执行的方法的实际代码。第五,创建一个新的激活帧,必要时进行同步,并将控制转移到方法代码

您提出的仅在将控制权转移到方法代码后才评估参数的情况是不可能的。

于 2013-08-12T07:08:38.717 回答