开发者

Everything's Final

开发者 https://www.devze.com 2023-01-12 18:24 出处:网络
I\'ve been using开发者_如何学JAVA PMD to help spot potential problems in my Java code, and I\'ve been finding its advice to be split between the useful, the idiosyncratic, and the \"WTF?!\".

I've been using开发者_如何学JAVA PMD to help spot potential problems in my Java code, and I've been finding its advice to be split between the useful, the idiosyncratic, and the "WTF?!".

One of the things it keeps telling me to do is to use the final keyword for literally every variable I can attach it to, including input parameters. For actual constants this seems sensible, but for other stuff it just strikes me as odd, possibly even a tad counterproductive.

Are there concrete advantages/disadvantages to hanging final on every variable declaration you possibly can?


"Every variable declaration you possibly can" sounds a bit extreme, but final is actually beneficial in many ways. Sometimes I wish that final was the default behavior, and required no keyword, but true "variables" required a variable modifier. Scala adopted something like this approach with its val and var keywords—using val (the final-like keyword) is strongly encouraged.

It is especially important to carefully consider whether each member variable is final, volatile, or neither, because the thread safety of the class depends on getting this right. Values assigned to final and volatile variables are always visible to other threads, without using a synchronized block.

For local variables, it's not as critical, but using final can help you reason about your code more clearly and avoid some mistakes. If you don't expect a value to change within a method, say so with final, and let the compiler find unnoticed violations of this expectation. I'm not aware of any that do currently, but it's easily conceivable that a JIT compiler could use this hint to improve performance too.

In practice, I don't declare local variables final whenever I could. I don't like the visual clutter and it seems cumbersome. But, that doesn't mean it's not something I should do.

A proposal has been made to add the var keyword to Java aimed at supporting type inference. But as part of that proposal, there have been a number of suggestions for additional ways of specifying local variable immutability. For example, one suggestion was to also add the key word val to declare an immutable variable with inferred type. Alternatively, some advocate using final and var together.


final tells the reader that the value or reference assigned first is the same at any time later.

As everything that CAN be final IS final in this scenario, a missing final tells the reader that the value will change later, and to take that into account.


This is a common idiom for tools like PMD. For example, below are the corresponding rules in Checkstyle. It's really a matter of style/preference and you could argue for both sides.

In my opinion, using final for method parameters and local variables (when applicable) is good style. The "design for extension" idiom is debatable.

  • http://checkstyle.sourceforge.net/config_misc.html#FinalParameters
  • http://checkstyle.sourceforge.net/config_design.html#DesignForExtension
  • http://checkstyle.sourceforge.net/config_coding.html#FinalLocalVariable


PMD also has option rules you can turn on that complains about final; it's an arbitrary rule.

If I'm doing a project where the API is being exported to another team - or to the world - leave the PMD rule as it stands. If you're just developing something that will forever and always be a closed API, disable the rule and save yourself some time.


Here are some reason why it may be beneficial to have almost everything tagged as final

Final Constants

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

This can be used then for other parts of your codes or accessed by other classes, that way if you would ever change the value you wouldn't have to change them one by one.

Final Variables

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

In this class, you build a scoped final variable that adds a prefix to the parameter environmentKey. In this case, the final variable is final only within the execution scope, which is different at each execution of the method. Each time the method is entered, the final is reconstructed. As soon as it is constructed, it cannot be changed during the scope of the method execution. This allows you to fix a variable in a method for the duration of the method. see below:

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

Final Constants

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

These are especially useful when you have really long lines of codes, and it will generate compiler error so you don't run into logic/business error when someone accidentally changes variables that shouldn't be changed.

Final Collections

The different case when we are talking about Collections, you need to set them as an unmodifiable.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

otherwise, if you don't set it as unmodifiable:

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

Final Classes and Final Methods cannot be extended or overwritten respectively.

EDIT: TO ADDRESS THE FINAL CLASS PROBLEM REGARDING ENCAPSULATION:

There are two ways to make a class final. The first is to use the keyword final in the class declaration:

public final class SomeClass {
  //  . . . Class contents
}

The second way to make a class final is to declare all of its constructors as private:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Marking it final saves you the trouble if finding out that it is actual a final, to demonstrate look at this Test class. looks public at first glance.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Unfortunately, since the only constructor of the class is private, it is impossible to extend this class. In the case of the Test class, there is no reason that the class should be final. The test class is a good example of how implicit final classes can cause problems.

So you should mark it final when you implicitly make a class final by making its constructor private.

0

精彩评论

暂无评论...
验证码 换一张
取 消