【问题标题】:Declare final variable, but set later声明最终变量,但稍后设置
【发布时间】:2012-07-19 23:24:28
【问题描述】:

我知道这是一个相当简单的话题,但我真的很想把它转过来。

这是我想要做的,但它不喜欢 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 Constructor vs onCreate

【问题讨论】:

  • 这是一个类似的例子(但局部变量,不是类字段),只是我偶然发现 - 它在 JDK 中 - java.util.PriorityQueue 中的一些代码似乎正在初始化最终变量分两步。在方法“public E poll()”中,在第 593 行声明了一个局部 final 变量(但未初始化),然后在下一行初始化。最终诠释 n;最终 E x = (E) es[(n = --size)];

标签: java android


【解决方案1】:
private final long mId;

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

【讨论】:

  • 技术错误。您可以使用脏反射技巧来删除 final 修饰符。
  • @andri:是的!那些黑客就在那里。
【解决方案2】:

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


或者,您可以将 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(..);
}

【讨论】:

  • 使其成为私有实例变量。并且正如 Bozho 所说,不要公开 setter 方法供其他类调用。
  • 谢谢。但这对于我使用它的目的来说似乎过分了
  • 矫枉过正的意思是我宁愿不浪费资源分配、垃圾和函数调用
  • 是的,正如我所说 - 你可能不需要它:) 但我把它放在了答案的完整性
  • 在我的代码中,我不仅要避免外部对象更改,还要避免内部意外更改值。这就是为什么我希望它是 finalprivate
【解决方案3】:

不可以的

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

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

【讨论】:

  • 我没有理由在这里点击首选项,因为这个活动是短暂的。谢谢
【解决方案4】:

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

【讨论】:

  • 你知道这背后的逻辑是什么吗? IMO 允许将其设置在班级中的一个位置是完全合理的。 ...但是当我想得更多时,如果您不立即设置该值,编译器无法保证它已被设置。
  • @GregT 没错,如果在您完成构造函数时未设置变量,则根本不会设置它,即使代码中可能只有一个地方设置它。保证在构造函数完成时只设置一次变量的唯一方法是允许它仅在构造函数或初始化程序中设置(无论如何它与所有构造函数合并,所以它和构造函数一样好)。
【解决方案5】:

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

public class ClassA {
   private final long mID;

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

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

【讨论】:

  • 重点是,他不能使用构造函数。他必须使用框架初始化方法(onCreate)
【解决方案6】:

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

【讨论】:

    【解决方案7】:

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

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

    getData()setData(T data) 方法必须是线程安全的。为了提供它,我们对wrapper 对象使用volatile 修饰符。读取 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;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 2013-10-04
      • 1970-01-01
      • 1970-01-01
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多