如何在finally块中访问返回值

Published: 11 Jun 2015 Category: java

尽管JVM是基于栈的虚拟机,但Java语言却不允许你去访问这个栈。但其实在某些场景下(尽管是不太常见),这个功能是非常实用的。

示例

方法返回的结果会存储在栈上。看下这段代码:

public int method() {
    if (something)
        return 1;

    ...
    if (somethingElse)
        return 2;

    ...
    return 0;
}

如果我们不考虑[停机问题](http://it.deepinmind.com/%5BHalting%20problem%5D(http://en.wikipedia.org/wiki/Halting_problem),错误处理,以及其它学术上的问题,可以这么说,上述方法“肯定”会返回0,1或者2。并且这个值会存储在栈上,然后才会跳出这个方法。

有时候还会有这么一种情况,就是希望在返回某个特定值的时候执行特定的操作。大家可能又会为 该不该使用多重return语句陷入无止境的口水仗了,这个方法就会变成这样:

public int method() {
    int result = 0;

    if (something)
        result = 1;

    ...
    if (somethingElse)
        result = 2;

    ...
    // Important action here prior to return
    if (result == 1337)
        log.info("hehehe ;-)");

    return result;
}

当然了,上面的方法是有问题的,因为如果if (something) return 1 执行了,那么 if (something) return 2就不会被执行了。为了能达到原来“单一return语句”那样的效果,我们得把代码重写成这样:

public int method() {
    int result = 0;

    if (something)
        result = 1;
    else {

        ...
        if (somethingElse)
            result = 2;
        else {
            ...
        }
    }

    // Important action here prior to return
    if (result == 1337)
        log.info("hehehe ;-)");

    return result;
}

当然了,关于这个方法 是否应该使用大括号以及缩进级别 还可以再吵上几天,不过这只是空费唇舌而已。

从栈中获取返回结果

其实我们想做的事情就是在原来的实现中,在返回结果之前的时候,检查下栈内的结果是什么,下面是用Java写的伪代码:

public int method() {
    try {
        if (something)
            return 1;

        ...
        if (somethingElse)
            return 2;

        ...
        return 0;
    }

    // Important action here prior to return
    finally {
        if (reflectionMagic.methodResult == 1337)
            log.info("hehehe ;-)");
    }
}

好消息就是,这个确实能实现!一个简单的小技巧就能实现这个功能:

public int method() {
    int result = 0;

    try {
        if (something)
            return result = 1;

        ...
        if (somethingElse)
            return result = 2;

        ...
        return result = 0;
    }

    // Important action here prior to return
    finally {
        if (result == 1337)
            log.info("hehehe ;-)");
    }
}

不过坏消息就是,你可千万别忘了给返回值赋值啊。不过不管怎么说,这项技术还是挺有用的,它可以实现“方法栈访问”,尽管Java语言并不允许我们这么做。

当然了

不过,当然你还可以通过下面这种方式来解决,虽然有点烦人:

public int method() {
    int result = actualMethod();

    if (result == 1337)
        log.info("hehehe ;-)");

    return result;
}

public int actualMethod() {
    if (something)
        return result = 1;

    ...
    if (somethingElse)
        return result = 2;

    ...
    return result = 0;
}

在很多时候,这种方案的确是要更好一些(可读性更强)。不过有时候可能你想要在finally块中做的并不止是打印日志这么简单,又或者你想访问的也不止是result的值,而你又不想再重构这个方法,这时候这项技巧就派上用场了。

还有别的方法吗

现在该看你的了。你有什么推荐的可选方案吗?比如说,使用try monad,或者是切面?

英文原文链接