When should one use final for method parameters an

2019-01-01 00:04发布

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?

标签: java final
16条回答
步步皆殇っ
2楼-- · 2019-01-01 00:22

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.

查看更多
栀子花@的思念
3楼-- · 2019-01-01 00:28

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"]))
查看更多
梦醉为红颜
4楼-- · 2019-01-01 00:30

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.
查看更多
浅入江南
5楼-- · 2019-01-01 00:30

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.

查看更多
回忆,回不去的记忆
6楼-- · 2019-01-01 00:30

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.

查看更多
千与千寻千般痛.
7楼-- · 2019-01-01 00:32

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.

查看更多
登录 后发表回答