【问题标题】:Final Local Variable may not have been initialized in anonymous inner class最终局部变量可能尚未在匿名内部类中初始化
【发布时间】:2012-04-20 01:26:50
【问题描述】:

这是我的代码:

final Foo myFoo = new Foo(new Inner() {
    @Override
    callback(){
         myFoo.bar();
    }
});

(带有实际函数名)

final MyArrayAdapter aa = new MyArrayAdapter(new View.OnClickListener() {
    @Override
    onClick(){
         aa.notifyDataSetChanged();
    }
});

Java 给我一个关于 myFoo 可能没有被初始化的错误。有没有什么办法解决这一问题?我可以在构造对象时将回调设置为 null,然后再对其进行更改,但我希望有一种更简洁的方法。有任何想法吗? (如果 Foo 不是我写的,也没有暴露接口来改变回调,那也是行不通的)

如果有人好奇,在我的具体场景中,Foo 是一个 ArrayAdapter,bar 是 notifyDataSetChanged()。适配器显示的内容取决于数组中项目的值,并且这些值在单击时会发生变化。回调是clickListener。

【问题讨论】:

  • 你不能只参考this.bar()bar()吗?
  • 不,我的实际代码比这个例子复杂,而且回调在另一个匿名内部类中
  • 为什么要覆盖 notifyDataSetChanged?从 API 文档看来,您应该调用 myFoo.registerDataSetObserver(new DataSetObserver(){})。
  • 当 ArrayAdapter 的数据中的一项发生变化时(由于点击它),ArrayAdapter 中其余的东西都需要重新绘制。有没有比调用 notifyDataSetChanged() 更好的方法?
  • 你需要将 myFoo 传递给 Inner 的构造函数。修复它。

标签: java callback anonymous-types final


【解决方案1】:

简短的回答是在 Java 中你绝对不能这样做,但编译器已经告诉你了。您试图一次创建两个相互引用的对象,这是一个先有鸡还是先有蛋的问题。最重要的是,您必须先创建其中一个。

建议是两步创建:

....
final MyArrayAdapter aa = new MyArrayAdapter();
aa.initializeButtonClickListener();
....

然后将'initialize'方法添加到适配器。

public void initializeButtonClickListener(){
    this.button.setOnClickListener(new View.OnClickListener() {
        @Override
        onClick(){
             notifyDataSetChanged();
        }
    });
}

因为构造有点复杂(即,比简单地调用构造函数更复杂),我建议将这两行代码拉入MyArrayAdapter 工厂方法,并将构造函数设为私有。

【讨论】:

  • 好的,我明白为什么这是不可能的了。我重写了它以在创建第一个对象后设置回调。我不知道为什么我不应该调用 notifyDataSetChanged(),但我想在数组的一个元素发生变化时重绘所有内容。
  • 嗯...我想我对您示例中的名称感到困惑。我认为 Inner 是一个 DataSetObserver,被 Adapter 调用,而不是调用它。 ClickListener(即Inner)正在监听的对象在哪里?我认为会有三行:创建一个 ArrayAdapter,创建一个调用 .notifyDataSetChanged 的​​ ClickListener,然后创建一个使用 ArrayAdapter 作为“模型”的“可点击”(我想是一个视图)对象,然后调用 view.setOnClickListener (听众)完成循环。
  • 好吧,我添加了一个带有我实际场景的版本。 clicklistener 附加到的按钮位于适配器内部。
  • @Drew 在这种情况下,我将在 MyArrayAdapter 上有一个名为 setOnClickListenerToButton 的委托方法,该方法调用 this.button.setOnClickListener()。我会适当地更改代码。我建议下一步可能是将复杂的创建拉到工厂方法中,但这取决于你。
  • 我选择了两步创建。 Factory 听起来是个好主意,但我不确定它将如何解决需要 2 个步骤的问题,我仍然无法构造两个相互引用的对象。
【解决方案2】:

这将在一般情况下有效且无需访问 Inner 定义,但您确实需要创建一个帮助程序类:

class CallbackHelper {
    private Foo foo;
    public void setFoo( Foo f ){ foo = f; }
    public Foo getFoo(){ return foo; }
}

然后像这样使用它:

final CallbackHelper cbh = new CallbackHelper();
Foo myFoo = new Foo(new Inner() {
    @Override
    callback(){
         cbh.getFoo().bar();
    }
});
cbh.setFoo( myFoo );

【讨论】:

    【解决方案3】:

    您可以在包含类中声明一个方法 callBarOnMyFoo 并从您的回调中调用它:

    void callBarOnMyFoo() {
        myFoo.bar();
    }
    

    然后你的回调需要调用callBarOnMyFoo,这是外部类的一个实例方法,Inner 代码应该有一个引用(我忘记了它的语法)。

    【讨论】:

    • myFoo 是一个局部变量,但 callBarOnMyFoo 无法访问它。我想我可以把它变成一个成员,但这似乎和我在构建后设置监听器的解决方案一样丑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多