没有在单例中调用构造函数



Hi我有以下代码,当我在调试器中逐步执行它时,我注意到构造函数没有被调用,因此在其中启动的mContext变量保持为null。

当我逐步执行调试器时,getInstance((函数将调用构造函数RaceTimeDataContract(上下文上下文(,但是,如果我尝试逐步进入构造函数,它不会,而是调试器逐步进入正在初始化TABLE_NAME的行。问题是由于mContext保持为null,所以抛出了异常。

有人知道是什么导致了这种行为吗?代码如下:

public class RaceTimeDataContract implements BaseColumns{
private static RaceTimeDataContract sInstance;
private static Context mContext;
private RaceTimeDataContract(Context context) {
this.mContext = context;   // This is not getting called
}
private RaceTimeDataContract(){}
public static RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
// mContext remains null and forces a exception
private final String TABLE_NAME = mContext.getResources().getString(R.string.table_name);

感谢您的反馈!

创建新实例时,在调用构造函数之前执行内联字段初始化。在您的情况下,一个简单的解决方案是在上下文设置后将字段初始化移动到构造函数中

例如(我对代码进行了一点重组,并在mContext上删除了this,因为它是静态成员(

public class RaceTimeDataContract implements BaseColumns {
private static RaceTimeDataContract sInstance;
private static Context mContext;
public static RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
private final String TABLE_NAME;
private RaceTimeDataContract(Context context) {
mContext = context;
this.TABLE_NAME = mContext.getResources().getString(R.string.table_name);
}
// The below is invalid as it doesn't set TABLE_NAME and should probably be removed
// private RaceTimeDataContract(){}
}

上面的内容不是很习惯,最好重新安排以更好地支持单例。有很多方法可以做到这一点,但是一个好处

  • 将上下文存储在实例对象上,而不是静态存储
  • 预防getInstance上的比赛条件
  • 正在删除默认构造函数

假设您不经常检索实例,则可以通过在getInstance调用上同步来轻松完成。例如

public class RaceTimeDataContract implements BaseColumns {
private static RaceTimeDataContract sInstance;
public static synchronized RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
private final Context mContext;
private final String tableName;
private RaceTimeDataContract(Context context) {
this.mContext = context;
tableName = mContext.getResources().getString(R.string.table_name);
}
}

最后要注意的是,单例在getInstance方法中使用参数但总是返回相同的对象是非典型的——在这种情况下,你可以想象,如果getInstance在不同的上下文中被第二次调用,那么我们得到的原始实例可能有不正确的表名。

如果您确实需要每个上下文一个singleton,那么您可以将每个上下文的每个实例存储在类似Map<Context, RaceTimeDataContract>的东西中,可能使用ConcurrentHashMap,并且最好将其视为某种引用缓存或工厂,而不是singletone。

如果可能的话,最好只有一个零参数的getInstance方法,它可以在第一次需要时静态地检索单例上下文。这可以将类打开为更典型/更简单的单例模式,如枚举单例和静态单例持有者。

最新更新