LoginSignup
1
0

More than 1 year has passed since last update.

【Android】Variable is accessed from within inner class, needs to be final or effectively final

Posted at

 自作の Android アプリで、画面上のボタンを押したら〇〇変数をOFFにする、というコードを作っていたら Variable 〇〇 is accessed from within inner class, needs to be final or effectively final というエラーが出た。
ONにしたい変数をグローバルにする、という簡単な対策でしたが、Android Studio で確認しました。

参考

Variable is accessed from within inner class, needs to be final or effectively final - stack overflow

試した環境

Android Studio Bumblebee | 2021.1.1 Patch 3

エラーの出る例

 onClickListenerの実装とその後の処理などを参考に、画面上のボタンを押したらboolean型変数 onActiveをOFFにする処理を考えました。
変数の宣言場所としては、Button buttonStartButton buttonStop と同列だったらいいかと思って以下のような位置にしました。

MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

        boolean onActive = true;    //  ここではNG

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive) break;
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive = false;
            }
        });
    }

残念ながら、この位置では、

Variable 'onActive' is accessed from within inner class, needs to be final or effectively final

が出ます。インナークラスから呼ばれるので、finalな変数にするか、事実上finalになるようにする必要あり、ってことですね。が、ボタンクリックで再代入したいのでfinalでは困ります。

解決策1

解決策の1つ目は、onCreate の外で変数を定義する(グローバル変数として宣言する)、です。

MainActivity.java
public class MainActivity extends AppCompatActivity {

    boolean onActive = true;    //    この位置で宣言

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

		onActive = true;
//      boolean onActive = true;    //  ここではNG

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive) break;
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive = false;
            }
        });
    }

解決策2

Android studioの提案する解決策は

Transform 'onActive' into final one element array

です。final宣言はするけど、配列にすれば要素の値を変えられる、ということのようですね。Android studio任せで修正してもらうと、こうなりました。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

        final boolean[] onActive = {true};    //  Android studio の quick fix
//      boolean onActive = true;    //  ここではNG

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive[0]) break;    // Android studio の quick fix、[0]付きに替わる
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive[0] = false;    // Android studio の quick fix、[0]付きに替わる    
            }
        });
    }

Stack Overflow にも、時間のないときに採用できる、必ずしも最高ではない、変わった(? 原文はfunny answer)対策として紹介されています。

おまけ

残念ながらこのサンプルは、Startボタンを押したら forループが終了するまで Stop ボタンの処理に移行しないので、Stopボタンを押しても breakでの中断はされません...
あくまでエラー回避のサンプルということでお願いします。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0