声纳 "useless assignment to local variable" 解决方法?

Sonar "useless assignment to local variable" workaround?

我正在努力改进我的代码,我从 Sonar 遇到了这个问题:

Remove this useless assignment to local variable "uiRequest"

事实是,它并非毫无用处,因为我在代码之后就使用了它:

        // I am supposed to remove this
        UiRequest uiRequest = null;

        if("Party".equals(vauban.getName()))   {
            uiRequest = contextBuilder.buildContext(vauban);
        } else {
            // Maybe I could work my way around here ?
            throw new NamingException(
                    String.format(
                            "Hey %s, change your name to %s, thanks",
                            vauban.getName(), "Vauban"));
        }

        // Set the generated Id in the result of context builder
        MyOwnService response = callService(uiRequest, vauban);

        return response;

Sonar 仍然告诉我 "uiRequest" 没用,为什么?它不是,因为如果它为空,我不希望它到达代码。我尝试初始化它 (uiRequest = new UiRequest()),但它一直告诉我它没用。

有人知道为什么 Sonar 会这样/如何纠正这个问题吗?

将它移到 if 语句中怎么样?

    if("Party".equals(vauban.getName()))   {
        UiRequest uiRequest = contextBuilder.buildContext(vauban);
        // Set the generated Id in the result of context builder
        MyOwnService response = callService(uiRequest, vauban);
        return response;
    } else {
        throw new NamingException(
                String.format(
                        "Hey %s, change your name to %s, thanks",
                        vauban.getName(), "Vauban"));
    }

或者在 if 语句中抛出异常而没有 else 子句。即让正常情况为 "happy path".

赋值是无用的,因为没有代码可以看到赋值。

您可以通过以下方式避免警告并可能使代码更具可读性:

// I am supposed to remove this
//    UiRequest uiRequest = null;  <-- remove

// invert the test here
    if(! "Party".equals(vauban.getName()))   {
        // Maybe I could work my way around here ?
        throw new NamingException(
                String.format(
                        "Hey %s, change your name to %s, thanks",
                        vauban.getName(), "Vauban"));
    } 

    // you are only using the variable in the call service; make
    //   final since reference should not change after assignment
    final UiRequest uiRequest = contextBuilder.buildContext(vauban);

    // Set the generated Id in the result of context builder
    MyOwnService response = callService(uiRequest, vauban);

    return response;

您的问题简化为:

Foo x = null;

if(a()) {
     x = b();
} else {
     throw new Exception();
}

c(x);

这段代码有两条可能的路径:

  1. a()returnstruex 被分配 b() 然后 c(x) 被调用。
  2. a()returnsfalse。抛出异常,不调用c(x)

这些路径都没有使用 null 的初始分配调用 c(x)。所以无论你一开始分配什么,都是多余的。

请注意,如果初始分配不是 null,这也会是一个问题。除非赋值的右侧有副作用,否则任何赋值都会被浪费。 (声纳分析副作用)

这对 Sonar 来说是可疑的:

  • 也许程序员希望第一个赋值有效果——它没有,所以这可能是一个错误。
  • 这还与代码清晰度有关——未来的代码人 reader 可能会浪费时间思考初始值的用途。
  • 如果右边有计算,但没有副作用,那就是浪费计算。

您可以通过两种方式解决此问题:

首先只删除 = null,留下 Foo x; - Java 足够聪明,可以意识到到 c(x) 的所有路由都涉及赋值,因此仍然可以编译.

更好的是,将 c(x) 移到块中:

if(a()) {
     Foo x = b();
     c(x);
} else {
     throw new Exception();
}

这在逻辑上是等价的,更简洁,并且缩小了 x 的范围。缩小范围是一件好事。当然,如果你需要x在更大的范围内,你就不能这样做了。

又一个变体,逻辑上也是等价的:

if(! a()) {
   throw new Exception();
}

Foo x = b();
c(x);

... 对 "extract-method" 和 "inline" 重构反应良好:

throwForInvalidA(...);
c(b());

使用最能传达您意图的那个。