理解Java内部类中的作用域
在一个Android项目中,我使用下面的代码。我得到一个错误:理解Java内部类中的作用域,java,android,scope,inner-classes,Java,Android,Scope,Inner Classes,在一个Android项目中,我使用下面的代码。我得到一个错误:变量tts可能尚未初始化。如果我更改了变量tts的声明位置,我就不会再得到错误。如果我注释掉内部OnInitListener类中引用tts的两行,我也不会再得到错误(但也不会发生任何有趣的事情) 因此,我推断,如果tts变量在封闭方法中声明(即使它被声明为final),则内部类无法“看到”它,但当它被声明为封闭类的实例变量时,它可以看到它 我来自JavaScript背景;显然,Java在此上下文中以不同的方式处理变量范围。如果您能解释
变量tts可能尚未初始化
。如果我更改了变量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);
}
}
});
}
}
package com.example.texttospeech;
导入android.speech.tts.TextToSpeech;
导入android.support.v7.app.ActionBarActivity;
导入android.os.Bundle;
导入java.util.HashMap;
导入java.util.Locale;
公共类MainActivity扩展了ActionBarActivity{
//private TextToSpeech tts;//取消对此的注释。。。
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testTextToSpeech();
}
私有void testTextToSpeech(){
final String toSpeak=getString(R.String.hello\u world);
final int mode=TextToSpeech.QUEUE\u FLUSH;
final HashMap HashMap=新HashMap();
最后一个文本:说出tts;//…并将其注释掉。。。
tts=新的TextToSpeech(getApplicationContext(),
新的TextToSpeech.OnInitListener(){
@凌驾
公共无效onInit(int状态){
if(状态!=TextToSpeech.ERROR){
//…或者简单地注释掉下面两行
tts.setLanguage(Locale.UK);
说话(toSpeak、mode、hashMap);
}
}
});
}
}
在内部,TextToSpeech.OnInitListener
实例将对tts
的引用传递给它的隐式构造函数,但此时,tts
尚未初始化,只是声明了(并且不能传递未初始化的变量(堆栈上的局部变量将始终是该变量)因为它没有值,所以它不适用于任何方法)。将新创建的TextToSpeech
类型的对象分配给变量tts
,该变量依赖于TextToSpeech.OnInitListener
(构造函数)的实例,该实例依赖于tts
->循环依赖关系
通过将
tts
声明为成员变量,它将自动初始化为null
。由于tts
是一个成员变量,TextToSpeech.OnInitListener
实例在内部传递对MainActivity
外部实例的隐式引用时,始终能够访问tts
的当前值,TextToSpeech.OnInitListener
实例将对tts
的引用传递给它的隐式构造函数,但此时,tts
尚未初始化,只是声明了(并且不能传递未初始化的变量(堆栈上的局部变量将始终是该变量)因为它没有值,所以它不适用于任何方法)。将新创建的TextToSpeech
类型的对象分配给变量tts
,该变量依赖于TextToSpeech.OnInitListener
(构造函数)的实例,该实例依赖于tts
->循环依赖关系
通过将
tts
声明为成员变量,它将自动初始化为null
。由于tts
是一个成员变量,TextToSpeech.OnInitListener
实例将始终能够访问tts
的当前值,因为它被传递到MainActivity
的外部实例的隐式引用,我认为以稍微不同的方式编写此代码就足够了,以查看这里发生了什么
final TextToSpeech tts;
TextToSpeech.OnInitListener listener = new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
tts.setLanguage(Locale.UK);
tts.speak(toSpeak, mode, hashMap);
}
}
tts = new TextToSpeech(getApplicationContext(), listener);
基本上,在侦听器
实例化的那一刻,声明了tts
,但它仍然不存在。这就是警告的内容。编译器不会向前看,如果您将tts
设置为类变量,那么据它所知,您的程序可能会在实例化tts
之前尝试使用listener
。通过将tts
声明放在同一本地范围内,可以避免这种风险
所以我建议做的是让
testTextToSpeech
返回对tts
的引用:
private TextToSpeech tts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = testTextToSpeech();
}
private TextToSpeech 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 = 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);
}
}
});
}
return tts;
}
private TextToSpeech tts;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts=testTextToSpeech();
}
私有TextToSpeech testTextToSpeech(){
final String toSpeak=getString(R.String.hello\u world);
final int mode=TextToSpeech.QUEUE\u FLUSH;
final HashMap HashMap=新HashMap();
final TextToSpeech tts=新TextToSpeech(getApplicationContext(),
新的TextToSpeech.OnInitListener(){
@凌驾
公共无效onInit(int状态){
if(状态!=TextToSpeech.ERROR){
tts.setLanguage(Locale.UK);
说话(toSpeak、mode、hashMap);
}
}
});
}
返回tts;
}
我认为用稍微不同的方式编写这段代码就足够了,看看这里发生了什么
final TextToSpeech tts;
TextToSpeech.OnInitListener listener = new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
tts.setLanguage(Locale.UK);
tts.speak(toSpeak, mode, hashMap);
}
}
tts = new TextToSpeech(getApplicationContext(), listener);
基本上,在侦听器
被实例化的那一刻,声明了tts
,但它仍然是d