在惰性初始化和一般情况下,重写是否可以有效地替换if语句



我正在尝试模拟以下抽象类,该类旨在只启用一个延迟初始化,而不使用逻辑语句。为了简单起见,我忽略了线程安全所必需的同步元素。

abstract class Thunk<T>
{
private boolean initiated = false;
private T value;
public T get()
{
if(!initiated) // not using (value == null)
{
value = compute();
initiated = true;
}
return value;
}
abstract protected T compute();
}

以下抽象类的实例是否会被子级多次入侵以初始化同一变量?

abstract class Thunk<T>
{
private T value;
private Computer<T> computer;
public Thunk()
{
computer = new Computer<T>(this);
}
public T get()
{
value = computer.getValue();    
return value;
}
abstract protected T compute();
private class Computer<T> 
{
private static final String TAG = "Computer";
private Thunk<T> thunk;
private T value;
private Computer<T> computer;
public Computer(Thunk<T> thunk)
{
Log.d(TAG, "constructed");
this.thunk = thunk;
computer = this;
}
public T getValue() 
{
Log.d(TAG + ".getValue()", "");
value = computer.computeValue();
return value;
}
protected T computeValue() 
{
Log.d(TAG + ".computeValue()", "");
value = thunk.compute();
computer = new DumbComputer<T>(thunk, value);
return value;
}
//this is for maximal encapsulation
private class DumbComputer<T> extends Computer<T>
{
private static final String TAG = "DumbComputer";
private T value;
public DumbComputer(Thunk<T> thunk, T value) 
{
super(thunk);
Log.d(TAG + ".contructed()", "booki");
this.value = value; 
}
//overriding so that value will be calculated only once.
@Override
protected T computeValue() 
{
Log.d(TAG + ".computeValue()", "");
return value;
}
}
}
}

是,通过重写get方法。

要修复此问题,可以将get设置为final方法。这将防止重写,并为您提供类似于singleton的行为。

请注意,您编写的代码不是线程安全的。

您可以通过制作方法synchronized来实现线程安全(不要担心性能,直到你知道你出了问题,并且方法是热点,因为慢速正确的代码比快速错误的代码好,JVM非常善于优化锁。如果你发现这个类的特定锁过热,你可以使用一些技巧来加速它…但现在还不用担心)

同样值得指出的是,如果你想要最好的singleton惰性init,那么可以使用惰性init的资源持有者内部类模式(不适用于你的用例,因为这个类需要。它只用于singleton)。

更新(回复评论,因为评论不支持格式化)

这样做:

abstract class Thunk<T>
{
private boolean initiated = false;
private T value;
public synchronized final T get()
{
if(!initiated) // not using (value == null)
{
value = compute();
initiated = true;
}
return value;
}
abstract protected T compute();
}

这是可能工作的最简单的代码。甚至不要梦想尝试"改进"那个代码。它是可以改进的,但根据类的使用方式,改进会有所不同,而且改进的复杂性会掩盖代码的意图。从最简单的可行方法开始,然后继续。

保持简单愚蠢的

不要解决你还没有的问题

模式

public final void f() {
...
X x = ...;
g(x);
...
}
abstract protected void g(X x);

在合同编程中非常有用:

  • 施加行为(f体),以及
  • 以提供本地上下文(x)

行为通常是通过保持一个状态(如initiated)来实现的。所以,是的,这对于懒惰的评估来说是可以的。尽管懒惰的评估可以在现场级别上实现,例如通过罕见的宝石Future<>

您的第二个示例(可能)没有按预期工作,因为每次调用Thunk.get时都会创建一个新的DumbComputer。你可以按照以下方式实现你的目标(但我认为这不是一个好的设计,我真的看不出与更简单的解决方案相比优势在哪里):

abstract class Thunk<T> {
T value;
Computer<T> computer;
protected abstract T doCompute ();
private interface Computer<T> {
Computer getComputer ();
T compute ();
}
public Thunk<T> () {
// initialize computer with a calculating one
computer = new Computer<T> () {
Computer getComputer () {
// return a dumb computer
return new Computer<T> () {
Computer getComputer () { return this; }
T compute () { return value; }
}
}
T compute () { value = doCompute (); return value; }
};
}
public T getValue () {
T v = computer.compute (); computer = computer.getComputer (); return v;
}
}

最新更新