無名クラスからローカル変数へのアクセス

こんなクラスがあって

class MyClass{
    void doSomething(){
        System.out.println("hoge");
    }
}

こんな感じでアクセスしようとすると

List<Runnable> createTasks(List<MyClass> instances){

    List<Runnable> tasks = new ArrayList<Runnable>();
    
    for (MyClass instance : instances) { //←ここのinstanceに
        
        Runnable task = new Runnable(){
            public void run(){
                instance.doSomething();//←ここでアクセス
            }
        };
        
        tasks.add(task);
    }
    
    return tasks;
}

コンパイルでエラーになります。

AnonymousRunnableSnip.java:42: ローカル変数 instance は内部クラスからアクセス
されます。final で宣言される必要があります。
                                        instance.doSomething();
                                        ^
エラー 1 個


そんな場合の対処法。以下の①で何をすればよいでしょうか?

for (MyClass instance : instances) {
    
    Runnable task = [①];

    tasks.add(task);
}

追記 これでいいじゃんということが判明

for (final MyClass instance : instances) {

よって以下は、
無駄無駄文となりました。。。。






1、ローカルクラスで対処

class MyTask implements Runnable{
    MyClass mc;
    MyTask(MyClass mc){
        this.mc = mc;
    }
    public void run(){
        mc.doSomething();
    }
}            
Runnable task = new MyTask(instance);

素直な(?)やり方。コンストラクタでインスタンスをわたす。

2、無名クラスで対処

Runnable task = new Runnable() {
    MyClass mc;

    public Runnable setData(MyClass mc) {
        this.mc = mc;
        return this;
    }

    public void run() {
        mc.doSomething();
    }
}.setData(instance);

Runnable task = ...に代入せずに、tasks.add(...)にわたせば、ローカルクラスよりやや文字数が少ない。
setData()で戻り値を返すのがミソ。

3、メソッドで対処

こんな感じのメソッド作って

private Runnable createTask(final MyClass mc){
    return new Runnable(){
        public void run(){
            mc.doSomething();
        }
    };
}

呼ぶ

Runnable task = createTask(instance);

ローカル変数でもfinalつければアクセスできるので。
ローカルクラスより、こっちの方が素直かな。

4、ジェネリックスなクラスで対処

こんな感じの共通クラス作っといて

abstract class RunnableWith<T> implements Runnable {
    private T instance;
    public RunnableWith(T instance){
        this.instance = instance;
    }
    public void run(){
        runWith(instance);
    }
    abstract void runWith(T instance);
}

それを使う

Runnable task = new RunnableWith<MyClass>(instance){
    public void runWith(MyClass mc){
        mc.doSomething();
    }
};

似たような処理がたくさんある場合は、毎回「値を渡す」処理を書く手間は省ける。


他にも対処あるかな?


補足

なお、値を渡す必要はないけど、コンストラクタみたいなとこで初期化処理をしたいって場合は、インスタンスイニシャライザ(コンストラクタもどきのブロック)が使えます。

Runnable task = new Runnable(){
    {
        //初期化処理←イニシャライザのブロック
    }
    public void run(){
        //処理
    }
};