Java - 不可变局部变量

3

默认情况下,将本地变量声明为 final 是一种常见的“良好实践”。不知道Eclipse是否有此功能,但在IDEA中,“创建本地变量”对话框中甚至有一个复选框。但是有一个问题让我无法始终使用它。以这段代码为例:

...
final Foo foo = null;
try{
    foo = getFromSomewhere();
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
}

if (foo != null) {
    doSomethingWithFoo(foo);
}

doSomethingElse();
...

问题在于 IDontCareException 没有继承 RuntimeException... 是否有办法仍然使用 final 变量?

只是为了确保我理解:您的IDE告诉您无法将IDontCareException e标记为final,因为IDontCareException没有扩展RuntimeException - sigpwned
1
foo不能是final,因为你将其设置为null,然后尝试重新分配它。当某物是常量时,你使用关键字“final”,而在你的情况下,它显然不是常量。 - Supericy
6
那就不要加上final关键字,代码仍然是正确的。我个人不认为这是一种好的做法。在代码中随处添加final关键字使得代码变得混乱。如果方法像它们应该的那样很短,将变量设置为final并没有增加任何东西,在我看来,反而降低了可读性并增加了冗余。 - JB Nizet
默认情况下将本地变量定义为final是一种常见的“良好实践”,只有在编写代码时确保您没有意外重新分配变量给其他内容时才这样做!!! - AllTooSir
1
@J-unior:因为它并没有真正回答你的问题。你要求一个可以使用final关键字的结构,而我的评论并没有告诉你如何做到。只是说我不在意使用它(以及原因)。 - JB Nizet
显示剩余4条评论
5个回答

2

尝试

final Foo foo;
try{
    foo = getFromSomewhere();
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
    foo = null;
}

if (foo != null) {
    doSomethingWithFoo(foo);
}

编辑:它不能编译。尝试这个。

Foo tmp;
try{
    tmp = getFromSomewhere();
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
    tmp = null;
}
final Foo foo = tmp;

if (foo != null) {
    doSomethingWithFoo(foo);
}

确实它无法编译 ^^ - mki
编译器并不像我想象的那么聪明:) - ZhongYu
1
好的,但是当我看到这段代码时,我真的相信添加final是不好的 :) - mki

2

final的定义是无法修改其所引用的内容。使用foo = getFromSomewhere()时,您就是这样做的。您不能那样做。一个选项是将所有内容都放在try块中,如下所示:

try{
    final Foo foo = getFromSomewhere();
    doSomethingWithFoo(foo); //If getFromSomewhere() always returns a non-null value, otherwise you will still need the null-check
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
}

doSomethingElse();
...

强制字段初始化和使用在同一位置。如果该字段确实只用于一次使用,那么它可以被定义为局部变量。 - radai

1
是的。编写一个辅助方法:
private Foo getFooOrNull() {
   try {
     return getFromSomewhere();
   } catch (Exception e) { return null;}
}

然后在你的类中:

private final Foo myFoo = getFooOrNull();

这将移动try/catch块,提高代码可读性,同时允许您保持字段为final。

留下“null”是没有帮助的。 - Tom Hawtin - tackline
是的,但之后你仍然需要知道是否有一个foo。另一种选择可能是一个什么都不做的模拟Foo,但那会更加复杂。 - radai

1

You could try for example:

private void myMethod(){
   try{
      final Foo foo = getFromSomewhere();
      if(foo != null){
         doSomethingWithFoo(foo);
      }
  } catch (IDontCareException e) {
   log.info(e, "looks like foo is not there);
  }
  doSomethingElse();
}

我假设 getFromSomewhere 不会返回 null。如果它确实返回了 null,那么可能应该将其重命名为类似于 maybeGetSomewhereOrNot 的名称,或者更有可能的是重写它。 - Tom Hawtin - tackline
@Tom Hawtin - tackline 这只是一个简单的假设,很多开发人员并不关心这种“命名”约定,尽管我同意你的看法。因此,对于这个简单的示例,它不关注 null 类型检查,并且没有明确的 getFromSomewhere() 源代码,检查空性并不是那么愚蠢。 - Mik378

1
一种实践方式是好的,如果它以某种方式改进了你的代码。毫无思考地将final添加到所有变量肯定不是一个好习惯。顺便说一下,局部变量已经在方法中有作用域,并且大多数时间寿命很短。为什么要使它们成为常量?这又是一种微观优化吗?老实说,这样做没有任何好处。而且会降低你的代码可读性。
final表示你的变量是常量,但在这里并非如此,因为你重新赋值了变量。
对我来说,在这里的正确答案是:不要将此变量设为final!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接