【问题标题】:How to make the variables of an Object final? [duplicate]如何使对象的变量最终? [复制]
【发布时间】:2017-10-15 13:48:12
【问题描述】:

我有一个关于 java 中的 final 变量的问题,我写了一个简短的代码来演示这个问题。 java8 的语言规范指出:

一旦分配了最终变量,它总是包含相同的 价值。如果最终变量持有对对象的引用,则 对象的状态可以通过对对象的操作来改变,但是 该变量将始终引用同一个对象。 Language Specification

为了进一步调查,我在一个小示例代码中尝试了三件事。我尝试的第一件事是创建Integer 类型的对象并为其分配非最终引用。然后我也分配了一个最终的参考。在第二个实验中,我对原始 int 做了同样的事情。这两个实验导致相同的结果,编译器不允许我增加最终引用,但允许增加非最终引用,并且在输出中只有非最终变量增加。

在我的第三个实验中,我使用了一个列表,并再次为列表分配了一个非最终引用和一个最终引用。在这里,我被允许使用最终引用和非最终引用来调用 add(),并且在这两个引用中,大小都已更新。

我的测试代码:

public void testFunction () {
    Integer nonFinalInteger = 4;
    final Integer finalInteger = nonFinalInteger;

    nonFinalInteger++;
    //Compiler shows error
    //finalInteger++;

    System.out.println("nonFinal Integer: " + nonFinalInteger);
    System.out.println("final Integer: " + finalInteger + "\n");

    int nonFinalInt = 4;
    final int finalInt = nonFinalInt;

    nonFinalInt++;

    //Compiler shows error
    //finalInt++;

    System.out.println("nonFinal primitive int: " + nonFinalInt);
    System.out.println("final primitive int: " + finalInt + "\n");

    List<String> nonFinalVar = new ArrayList<String>();
    final List<String> finalVar = nonFinalVar;

    finalVar.add("Hello");
    //Compiler does not show error
    nonFinalVar.add("World");

    System.out.println("nonFinal List Size: " + nonFinalVar.size());
    System.out.println("final List Size: " + finalVar.size() + "\n");
}

输出:

nonFinal Integer: 5
final Integer: 4

nonFinal primitive int: 5
final primitive int: 4

nonFinal List Size: 2
final List Size: 2

我现在的问题是:有没有一种方法可以保护对象的状态而无需更改其类的代码?假设我们停留在存在List 的上下文中,您不应该被允许添加或删除元素。是否可以以某种方式标记列表,以便编译器显示错误(或至少某种警告)?

编辑:

似乎我在示例中使用数组引起了一些混淆。我的问题应该涉及任何类型的对象,因此我没有在问题或标签中放入 List 。我的意思不是告诉编译器更新引用是被禁止的,我只是想知道是否有一种方法可以隐式告诉编译器拒绝调用可能改变对象状态的函数。

目前包装类的想法似乎是最吸引人的,尽管它对我来说仍然缺少一件事:它不会阻止我或其他人完全改变对象的状态。最终变量总是具有我在这里寻找的这种“不接触”特性。

【问题讨论】:

  • 你可以把你的对象放在一个不可变的包装类中,没有setter和一个只返回原始对象副本的getter。原始对象无法更改,但您需要记住返回的副本仍然可以更改,
  • 看看herehere
  • 您将无法产生编译时错误,因为对于任何有效代码(例如在不可修改的对象上调用 add/remove),代码都会成功编译。你可以做一个 RuntimeException
  • 太糟糕了,不可能出现编译时错误。包装解决方案并不完全令人满意,但它似乎是我必须使用的解决方案。附言我已对帖子添加了编辑,很抱歉让您感到困惑,但链接为重复的问题并没有为我的情况提供答案
  • 我会选择不可变的列表类。它们在多个库中可用,例如 guava (google common) 或 eclipse-collections。虽然他们在使用 add 函数时不会发出编译时错误

标签: java constants final


【解决方案1】:

这是一个令人困惑的问题。我会试着解释一下:

当你将一个变量声明为final时,结果是他的值可以以两种不同的形式赋值:

  • 在定义最终变量的同一行中分配值。
  • 如果最终变量BA类的属性,那么B的值可以在A类的构造函数中赋值。

但重要的是,当我说赋值时,我指的是以下形式的赋值: final ArrayList&lt;Integet&gt; myArray = new ArrayList&lt;Integer&gt;(Arrays.asList(1,2,3));

现在变量 myArray 始终指向该数组列表,并且不能再次以相同的形式分配: myArray = new ArrayList&lt;Integer&gt;(Arrays.asList(4,5,6)); 会抛出错误。 但话虽如此,您仍然可以在该变量中进行操作。例如,如您所见,您可以添加值:myArray.add(4);

简而言之:当您将变量 C 标记为 final 时,您就是这样做的,即不能将 C 分配给另一个对象,但该目标对象可以随时改变他的状态。

【讨论】:

    猜你喜欢
    • 2018-04-18
    • 2018-09-15
    • 2013-11-26
    • 2017-10-27
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    相关资源
    最近更新 更多