在下面的代码中,如果我从EditText中删除关键字final,我在第(6)行中遇到错误,我将EditText对象(et)传递给intent ...我必须知道final关键字的重要性...
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);
}
});
答案 0 :(得分:21)
最终本质上意味着变量et
不会在任何时候重新分配并保持不变。这意味着内部类(如侦听器)可以相信它不会被其他可能导致各种麻烦的线程重新分配。
final也可用于修改方法或类定义,这意味着该方法不能被子类覆盖,或者该类无法扩展。
答案 1 :(得分:15)
这是因为你在这里使用了闭包。这意味着内部类使用了内部类的上下文。要使用它,变量应该被声明为final,以便不被更改。
查看更多here。
答案 2 :(得分:3)
阅读this article以了解所涉及的实施细节:
这种限制的原因 如果我们发现一些亮光就会变得明显 如何实施本地类。 匿名本地类可以使用本地 变量因为编译器 自动给出一个班级 用于保存副本的私有实例字段 该类使用的每个局部变量。 编译器还添加了隐藏 每个构造函数的参数 初始化这些自动创建 私人领域。因此,一个本地班 实际上并不访问本地 变量,但仅仅是它自己的私有 他们的副本。唯一的方法就是这样 工作正确是如果当地的 变量被声明为final,所以 他们保证不会改变。 有了这个保证到位, 当地的班级确信它的 变量的内部副本 准确反映当地的实际情况 变量
编辑:
柏林布朗说:“我发布了一个反编译版本 匿名的内部阶级。但是要 老实说,我还是不明白为什么 编译器必须拥有该信息。 即使该领域被宣布为最终, 该字段仍然可以为null。我认为 这是Java怪癖中的一个,你 必须声明该字段 最终...因为这就是它的方式。 没有明确的理由为什么“
原因是要确保用户意识到闭包“关闭”变量而不是值。我们假设没有要求最终的局部变量。然后我们可以编写如下代码:
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
}
您认为输出是什么?如果你认为它会是“0 1 2”你会错的,因为Runnable关闭了“变量”i而不是那个时间点的i的“值”,因此输出将是“2 2 2”。在这里可以做些什么来实现预期的行为?两种解决方案:要么依赖用户了解闭包的工作方式,要么在语言层面以某种方式强制执行闭包。这是语言设计师使用的第二种选择。
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,我并不是说第二个选项是“走向”的方式,只是在用于匿名内部类之前将局部变量标记为final是对我来说是一个很大的障碍。当然,YMMV。 : - )
答案 3 :(得分:2)
最终使变量et只允许分配一次。它还会更改变量的范围,并允许函数onClick可见性等。如果没有最终结果,则在onClick函数中看不到et。
答案 4 :(得分:2)
JAVA中“final”关键字的目的可以在三个级别定义,分类,方法,变量
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.
答案 5 :(得分:0)
Here is a link讨论final
关键字。根据{{3}}(第4.12.4节):
变量可以声明为final。最终变量只能分配一次。如果分配了最终变量,则编译时错误,除非在分配之前它是明确未分配的(第16段)。
答案 6 :(得分:0)
它还有助于理解Java如何创建匿名内部类。 Java如何实现该特定类。
Java需要该变量为final,因为编译器必须在编译时知道该对象的类型。然后编译器将在匿名内部类实现中生成一个字段(在本例中为'et')。
如果类型不是final,那么编译器将如何确定如何构建内部类实现。基本上,通过声明字段final,您将为Java编译器提供更多信息。
无法帮助编译器的代码,不会编译您的匿名内部类:
Object et;
et = a ? new Object() : new EditText();
...
使用上面的代码,Java编译器无法真正构建匿名内部类。
您的代码:
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);
}
});
... java编译器将创建一个字节码类,这是您提供的内部类块的实现,可能看起来像这样。
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);
}
}
您可以通过查找匿名字节码类$ 1并对该字节码文件进行反编译来测试我提供的伪代码。