尽管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,或者是切面?