了解Java内部类中的范围

Jam*_*ton 1 java android scope inner-classes

在Android项目中,我使用下面的代码.我收到错误:variable tts might not have been initialized.如果我改变声明变量tts的位置,我不再得到错误.如果我注释掉在内部OnInitListener类中引用tts的两行,我也不再得到错误(但是没有任何有趣的事情发生).

因此,我推断内部类不能"看到" tts变量,如果它在封闭方法中声明(即使它被声明为final),但是当它被声明为封闭类的实例变量时它可以看到它.

我来自JavaScript背景; 显然,Java在这种情况下以不同的方式处理变量范围.如果你能解释一下Java正在做什么,我将不胜感激,这样我就能理解这些差异.

package com.example.texttospeech;

import android.speech.tts.TextToSpeech;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import java.util.HashMap;
import java.util.Locale;


public class MainActivity extends ActionBarActivity {

    //private TextToSpeech tts; // UNCOMMENT THIS...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testTextToSpeech();
    }

    private void testTextToSpeech() {
        final String toSpeak = getString(R.string.hello_world);
        final int mode = TextToSpeech.QUEUE_FLUSH;
        final HashMap hashMap = new HashMap<String, String>();

        final TextToSpeech tts; // ... AND COMMENT THIS OUT...

        tts = new TextToSpeech(getApplicationContext(),
                new TextToSpeech.OnInitListener() {
                    @Override
                    public void onInit(int status) {
                        if (status != TextToSpeech.ERROR) {
                            // ... OR SIMPLY COMMENT OUT THE NEXT TWO LINES
                            tts.setLanguage(Locale.UK);
                            tts.speak(toSpeak, mode, hashMap);
                        }
                    }
                });
    }
}
Run Code Online (Sandbox Code Playgroud)

Nam*_*ace 5

- #1方法中的所有最终变量都隐式复制为onInitClass的字段.它还有一个外部类的隐式字段.所有这些都可能是意外内存泄漏的来源,而泄漏则意味着无意中创建了一个阻止垃圾收集器完成其工作的引用,因此您需要在最终声明的内容中小心.

- #2它说tts可能尚未初始化的原因是因为你在为它赋值之前使用它.Final使引用成为不可变的,因此只能在声明时将值赋给final.如果你没有赋值,那就意味着占用内存空间的内容很可能是上次使用内存空间存储值时的随机垃圾.您可以显式设置tts = null,但是设置为null的最终变量完全无用(无法更改),因此您需要在声明它的同一行中设置其实际值.

例如,最终的TextToSpeech tts = new TextToSpeech(getApplicationContext(),listener);

- #3你的内部类可以引用外部类中的变量.你要做的就是写OuterClass.this.myfield;

所以这可能是重写代码的可行方法.

TextToSpeech tts;  //class field

private void testTextToSpeech() {
        final String toSpeak = getString(R.string.hello_world);
        final int mode = TextToSpeech.QUEUE_FLUSH;
        final HashMap<String, String> hashMap = new HashMap<String, String>();

        tts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {

                @Override
                public void onInit(int status) {
                    if (status != TextToSpeech.ERROR) {

                        tts.setLanguage(Locale.UK);
                        tts.speak(toSpeak, mode, hashMap);
                    }
                }
            }); 
}
Run Code Online (Sandbox Code Playgroud)