When should one use final for method parameters an

2018-12-31 23:58发布

问题:

I\'ve found a couple of references (for example) that suggest using final as much as possible and I\'m wondering how important that is. This is mainly in the the context of method parameters and local variables, not final methods or classes. For constants, it makes obvious sense.

On one hand, the compiler can make some optimizations and it makes the programmer\'s intent clearer. On the other hand, it adds verbosity and the optimizations may be trivial.

Is it something I should make an effort to remember to do?

回答1:

Obsess over:

  • Final fields - Marking fields as final forces them to be set by end of construction, making that field reference immutable. This allows safe publication of fields and can avoid the need for synchronization on later reads. (Note that for an object reference, only the field reference is immutable - things that object reference refers to can still change and that affects the immutability.)
  • Final static fields - Although I use enums now for many of the cases where I used to use static final fields.

Consider but use judiciously:

  • Final classes - Framework/API design is the only case where I consider it.
  • Final methods - Basically same as final classes. If you\'re using template method patterns like crazy and marking stuff final, you\'re probably relying too much on inheritance and not enough on delegation.

Ignore unless feeling anal:

  • Method parameters and local variables - I RARELY do this largely because I\'m lazy and I find it clutters the code. I will fully admit that marking parameters and local variables that I\'m not going to modify is \"righter\". I wish it was the default. But it isn\'t and I find the code more difficult to understand with finals all over. If I\'m in someone else\'s code, I\'m not going to pull them out but if I\'m writing new code I won\'t put them in. One exception is the case where you have to mark something final so you can access it from within an anonymous inner class.


回答2:

Is it something I should make an effort to remember to do?

No, if you are using Eclipse, because you can configure a Save Action to automatically add these final modifiers for you. Then you get the benefits for less effort.



回答3:

The development-time benefits of \"final\" are at least as significant as the run-time benefits. It tells future editors of the code something about your intentions.

Marking a class \"final\" indicates that you\'ve not made an effort during design or implementation of the class to handle extension gracefully. If the readers can make changes to the class, and want to remove the \"final\" modifier, they can do so at their own risk. It\'s up to them to make sure the class will handle extension well.

Marking a variable \"final\" (and assigning it in the constructor) is useful with dependency injection. It indicates the \"collaborator\" nature of the variable.

Marking a method \"final\" is useful in abstract classes. It clearly delineates where the extension points are.



回答4:

I\'ve found marking method parameters and locals as final is useful as a refactoring aid when the method in question is an incomprehensible mess several pages long. Sprinkle final liberally, see what \"cannot assign to final variable\" errors the compiler (or your IDE) throws up, and you just might discover why the variable called \"data\" ends up null even though several (out of date) comments swear that can\'t happen.

Then you can fix some of the errors by replacing the reused variables with new variables declared closer to the point of use. Then you find you can wrap whole parts of the method in scoping braces, and suddenly you\'re one IDE keypress away from \"Extract Method\" and your monster just got more comprehensible.

If your method is not already an unmaintainable wreck, I guess there might be value in making stuff final to discourage people from turning it into said wreck; but if it\'s a short method (see: not unmaintainable) then you risk adding a lot of verbosity. In particular, Java function signatures are hard enough to fit into 80 characters as it is without adding six more per argument!



回答5:

Well, this all depends on your style... if you LIKE seeing the final when you won\'t be modifying the variable, then use it. If you DON\'T LIKE seeing it... then leave it out.

I personally like as little verbosity as possible, so I tend to avoid using extra keywords that aren\'t really necessary.

I prefer dynamic languages though, so it\'s probably no surprise I like to avoid verbosity.

So, I would say just pick the direction you are leaning towards and just go with it (whatever the case, try to be consistent).


As a side note, I have worked on projects that both use and don\'t use such a pattern, and I have seen no difference in the amount of bugs or errors... I don\'t think it is a pattern that will hugely improve your bug count or anything, but again it is style, and if you like expressing the intent that you won\'t modify it, then go ahead and use it.



回答6:

I use final all the time to make Java more expression based. See Java\'s conditions (if,else,switch) are not expression based which I have always hated especially if your used to functional programming (ie ML, Scala or Lisp).

Thus you should try to always (IMHO) use final variables when using conditions.

Let me give you an example:

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = \"Candidate Stuff\";
            break;
        case JOB_POSTING_IMPORT:
            name = \"Blah\";
            break;
        default:
            throw new IllegalStateException();
    }

Now If add another case statement and do not set name the compiler will fail. The compiler will also fail if you do not break on every case (that you set the variable). This allows you to make Java very similar to Lisp\'s let expressions and makes it so your code is not massively indented (because of lexical scoping variables).

And as @Recurse noted (but apparently -1 me) you can do the preceding with out making String name final to get the compiler error (which I never said you couldn\'t) but you could easily make the compiler error go away setting name after the switch statement which throws away the expression semantics or worse forgetting to break which you cannot cause an error (despite what @Recurse says) without using final:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = \"Candidate Stuff\";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = \"Blah\";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = \"Whoops bug\";

Because of the bug setting name (besides forgetting to break which also another bug) I can now accidentally do this:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = \"Candidate Stuff\";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = \"Whoops bug\";

The final variable forces a single evaluation of what name should be. Similar to how a function that has a return value must always return a value (ignoring exceptions) the name switch block will have to resolve name and thus bound to that switch block which makes refactoring chunks of code easier (ie Eclipe refactor: extract method).

The above in OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> \"Candidate Stuff\"
    | JobPostingImport -> \"Blah\" ;;

The match ... with ... evaluates like a function ie expression. Notice how it looks like our switch statement.

Here is an example in Scheme (Racket or Chicken):

(define name 
    (match b
      [\'CandidateExport \"Candidate Stuff\"]
      [\'JobPostingImport \"Blah\"]))


回答7:

If you are writing a application that someone will have to read the code after, say, 1 year, then yes, use final on variable that should not be modified all the time. By doing this, your code will be more \"self-documenting\" and you also reduce the chance for other developers to do silly things like using a local constant as a local temporary variable.

If you\'re writing some throwaway code, then, nah, don\'t bother to identify all the constant and make them final.



回答8:

It is useful in parameters to avoid change the parameter value by accident and introduce a subtle bug. I use to ignore this recommendation but after spending some 4 hrs. in a horrible method ( with hundreds of lines of code and multiple fors, nested ifs and all sort of bad practices ) I would recommend you to do it.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }

Of course in a perfect world this wouldn\'t happen, but.. well.. sometimes you have to support others code. :(



回答9:

I will use final as much as I can. Doing so will flag if you unintentionally change the field. I also set Method parameters to final. Doing so I have caught several bug from code I have taken over when they try to \'set\' a parameter forgetting Java passes by value.



回答10:

It\'s not clear from the question whether this is obvious, but making a method parameter final affects only the body of the method. It does NOT convey any interesting information about the method\'s intentions to the invoker. The object being passed in can still be mutated within the method (finals are not consts), and the scope of the variable is within the method.

To answer your precise question, I wouldn\'t bother making an instance or local variable (including method parameters) final unless the code required it (e.g. the variable is referenced from an inner class), or to clarify some really complicated logic.

For instance variables, I would make them final if they are logically constants.



回答11:

There are many uses for the variable final. Here are just a few

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 in to logic/business error when someone accidentally changes variables that shouldn\'t be changed.

Final Collections

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 it\'s constructor private.



回答12:

Somewhat of a trade-off as you mention, but I prefer explicit use of something over implicit use. This will help remove some ambiguity for future maintainers of code - even if it is just you.



回答13:

If you have inner (anonymous) classes, and the method needs to access variable of the containing method, you need to have that variable as final.

Other than that, what you\'ve said is right.



回答14:

Use final keyword for a variable if you are making that variable as immutable

By declaring the variable as final, it aids developers to rule out possible modification issues of variables in highly multi-threaded environment.

With java 8 release, we have one more concept called \"effectively final variable\". A non-final variable can heave as final variable.

local variables referenced from a lambda expression must be final or effectively final

A variable is considered effective final if it is not modified after initialization in the local block. This means you can now use the local variable without final keyword inside an anonymous class or lambda expression, provided they must be effectively final.

Till Java 7, you cannot use a non-final local variable inside an anonymous class, but from Java 8 you can

Have a look at this article



回答15:

A very simple answer we have 3 cases with Final with variables ,Final with methods && Final with classes..

1.Final with variable: u can`t assign this variable more than one time..

2.Final with methods : u can`t override this method..

3.Final with classes : u can`t extend any final class



回答16:

First of all, the final keyword is used to make a variable constant. Constant means it does not change. For example:

final int CM_PER_INCH = 2.54;

You would declare the variable final because a centimeter per inch does not change.

If you try to override a final value, the variable is what it was declared first. For example:

final String helloworld = \"Hello World\";
helloworld = \"A String\"; //helloworld still equals \"Hello World\"

There is a compile error that is something like:

local variable is accessed from inner class, must be declared final

If your variable cannot be declared final or if you don\'t want to declare it final try this:

final String[] helloworld = new String[1];
helloworld[0] = \"Hello World!\";
System.out.println(helloworld[0]);
helloworld[0] = \"A String\";
System.out.println(helloworld[0]);

This will print:

Hello World!
A String


标签: java final