what is the use of keyword final?

2019-03-25 14:53发布

问题:

In the below code if i remove the keyword final from EditText i am an getting error in the line (6) where i pass EditText object (et) to the intent...I have to knw the significance of final keyword here...

final EditText et=(EditText)findViewById(R.id.t);
        Button b=(Button)findViewById(R.id.b1);
        b.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

回答1:

It is because you use closure here. It means that inner class uses the context of the inbounded one. To use it the variables should be declared final in order not to be changed.

See more here.



回答2:

Final essentially means that the variable et will not be reassigned at any point and will remain around. This means that inner classes, like your listener, can trust that it wont be reassigned by some other thread which could cause all kinds of trouble.

final can also be used to modify a method or class definition, that would mean that the method can't be overriden by a subclass, or that the class cannot be extended.



回答3:

Read this article to understand the implementation details involved:

The reason for this restriction becomes apparent if we shed some light on how local classes are implemented. An anonymous local class can use local variables because the compiler automatically gives the class a private instance field to hold a copy of each local variable the class uses. The compiler also adds hidden parameters to each constructor to initialize these automatically created private fields. Thus, a local class does not actually access local variables, but merely its own private copies of them. The only way this can work correctly is if the local variables are declared final, so that they are guaranteed not to change. With this guarantee in place, the local class is assured that its internal copies of the variables accurately reflect the actual local variables.

EDIT:

Berlin Brown says: "I posted a decompiled version of an anonymous inner class. But to be honest, I still don't see why the compiler has to have that information. Even if the field is declared final, the field can still be null. I think this is one of those Java quirks, you have to declare that field final...because that is the way it is. There isn't a clear reason why"

The reason is to make sure that users realize that closures "close over" variables and not values. Let's suppose that there was no requirement of having final local variables. Then we could write code like:

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(i);
            }
        });
    }
    run(runnables); // run each runnable
}

What do you think would be the output? If you think it would be "0 1 2" you would be mistaken since the Runnable closes over the "variable" i and not the "value" of i at that point in time and hence the output would be "2 2 2". What can be done to achieve the expected behaviour here? Two solutions: either rely on the users to have an understanding of how closures work or somehow enforce it at the language level. And it is the second option with which the language designers have gone with.

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        final int j = i; // notice the final local variable
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(j);
            }
        });
    }
    run(runnables);
}

JFTR, I'm not saying that the second option is "the" way to go, it's just that having local variables marked as final before being used in anonymous inner classes is a big deal breaker for me. Of course, YMMV. :-)



回答4:

Final makes the variable et only allowed to be assigned once. It also changes the scope of the variable and allows the function onClick visibility to et. Without the final, et is not visible within the function onClick.



回答5:

The purpose of “final” keyword in JAVA can be defined in three level are Class, Method, variable

Java final variable: If you make any variable as final, you cannot change the value of final variable (It will be constant).

Java final method: If you make any method as final, you cannot override it.

Java final class: If you make any class as final, you cannot extend it.


回答6:

Here is a link which discusses the final keyword. According to the specification (section 4.12.4):

A variable can be declared final. A final variable may only be assigned to once. It is a compile time error if a final variable is assigned to unless it is definitely unassigned (§16) immediately prior to the assignment.



回答7:

It also helps to understand how Java creates an anonymous inner class. How Java implements that particular class.

Java needs for that variable to be final because the compiler must know the type of that object at compile time. The compiler will then generate a field within the anonymous inner class implementation (in this case 'et').

If the type is not final, how would the compiler determine how to build the inner class implementation. Basically, by declaring the field final, you are giving the Java compiler more information.

Code that doesn't help the compiler, won't compile your anonymous inner class:

Object et;
et = a ? new Object() : new EditText();

...

With the code above, the Java compiler cannot really build the anonymous inner class.

Your code:

final EditText et=(EditText)findViewById(R.id.t);

...

new Button.OnClickListener$1(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

... The java compiler will create a bytecode class, an implementation of that inner class block that you provided that might look like this.

public class OnClickListener$1 {

 private final EditText et ; <---- this is important
 public OnClickListener$1(final et) {
    this.et = et;
 }
 public void onClick(View v)<br>
                {
                Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
                startActivity(on);
                }
}

You can test the pseudo code I provided by finding the anonymous bytecode class $1 and decompile that bytecode file.