47

我知道这是一个相当简单的话题,但我真的很想绕开它。

这就是我想要做的,但它不喜欢 final 修饰符。有没有其他方法可以达到我想要的效果?这基本上是我想确保 id 在活动的整个生命周期中都不会改变。

private final long mId;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mId = getIntent().getLongExtra(ID_KEY, -1);
}

我应该指出,这是 Android 代码。感谢所有的帮助。我不担心 getter 或 setter 或任何人更改我的代码。我问的原因是为了让下一个开发人员接管我的代码。我发现这篇文章也有助于阐明一些观点。Android - Activity 构造函数与 onCreate

4

7 回答 7

62

final只能在构造函数或初始化程序中设置变量。常规方法不能更改声明的变量的值final

于 2012-07-20T16:39:37.123 回答
22

你不能。但是如果它是私有的并且你没有设置器,你可以保证没有外部对象改变它。


或者,您可以将 long 值包装在另一个类中 - LazyImmutableLong。但这是一种更冗长的方法,您可能不需要它(注意:下面的类不是线程安全的)

class LazyImmutableLong {

    private Long value;

    public void setValue(long value) {
         if (this.value != null) {
             return; // the value has already been set
         }
         this.value = value;
    }
    public long getValue() {return value;}
}

在你的活动中

private LazyImmutableLong id = new LazyImmutableLong();

public void onCreate(..) {
    id.setValue(..);
}
于 2012-07-20T16:37:47.070 回答
5

以下Worm(Write-Once-Read-Many)类可以在这种情况下提供帮助。

我们可以创建一个嵌套Wrapper类,它存储您需要的最终变量。要初始化此变量,您只需调用wrapper对象的构造函数。当您调用该方法getData()时,您将获得最终变量的引用,以防它被初始化,否则,您将获得null.

方法getData()setData(T data)必须是线程安全的。为了提供它,我们使用对象的volatile修饰符wrapper。读取 volatile 变量是同步的,写入 volatile 变量也是同步的。尽管为使这段代码线程安全做了一些努力,但我没有在这方面对其进行测试。根据线程安全级别,您可以考虑使 setter 和 getter 同步。

public class Worm<T> {
    private volatile Wrapper<T> wrapper;

    public Worm() {}
    public Worm(T data) throws IllegalAccessError
    {
        setData(data);
    }

    public T getData()
    {
        if (wrapper == null)
            return null;
        return wrapper.data;
    }

    public void setData(T data) throws IllegalAccessError
    {
        if (wrapper != null)
            throw new IllegalAccessError();
        else
            wrapper = this.new Wrapper<>(data);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Worm<T> other = (Worm<T>) obj;
        return Objects.equals(this.getData(), other.getData());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(this.getData());
    }

    final private class Wrapper<T> {
        final private T data;

        Wrapper(T data) {
            this.data = data;
        }
    }
}
于 2016-07-10T09:56:47.310 回答
0
private final long mId;

final根据 java 规范,不能在运行时修改引用。因此,一旦您将其声明为 final,mId 就不能在其整个生命周期中指向其他内容(除非您使用反射(或)将值包装在对象中并通过其他引用对其进行修改)。

于 2012-07-20T16:37:28.550 回答
0

您以后只能在构造函数中设置全局最终变量。例子:

public class ClassA {
   private final long mID;

   public ClassA(final long mID) {
      this.mID = mID;
   }
}

在这种情况下,您必须在每个构造函数中初始化最终变量。

于 2012-07-20T16:40:30.373 回答
0

您必须在创建构造函数后立即对其进行初始化,或者您可以在构造函数中将其初始化为最大值。不迟。。

于 2012-07-20T16:41:53.280 回答
-1

不,它不能完成

如果你可以在一个地方声明final,稍后再初始化,那么final是什么意思。

如果您想拥有一个常量 ID,为什么不使用 Shared Preferences ,将其存储在 SP 中并随时检索。

于 2012-07-20T16:39:06.703 回答