定义 final 一个不可变参数,将参数绑定到另一个局部变量并改变局部变量是否合法?

Is it legitimate to define final an immutable parameter, bind the parameter to another local variable and mutate the local variable?

请注意:同样的问题适用于其他不可变类型,例如 String 和 Boolean。


我有一个这样的方法(这是一个更复杂方法的简单例子):

示例 1

public BigDecimal addTwo(BigDecimal bigDecimal) {
    bigDecimal = bigDecimal.add(new BigDecimal(2));
    return bigDecimal;
}

我知道,我可以 return bigDecimal.add(new BigDecimal(2))。但这只是一个例子。

此代码的问题是我无法将 final 添加到方法参数中,Eclipse 困扰着我。所以我会写:

示例 2

public BigDecimal addTwo(final BigDecimal bigDecimal) {
    BigDecimal bigDecimalLocal = bigDecimal;
    bigDecimalLocal = bigDecimalLocal.add(new BigDecimal(2));
    return bigDecimalLocal;
}

我知道,我可以直接做BigDecimal bigDecimalLocal = bigDecimal.add(new BigDecimal(2))。但我再说一遍,这只是一个例子。

问题是:当我做的时候:

BigDecimal bigDecimalLocal = bigDecimal;

不是创建一个新的BigDecimal。我将同一个对象分配给不同的变量。我 克隆 BigDecimal 的简单方法是:

示例 3

BigDecimal bigDecimalLocal = new BigDecimal(bigDecimal.toString());

问题是:既然BigDecimal是不可变的,这真的有必要吗?我不能像示例 #2 那样简单地做吗?我认为 final 关键字不能以这种方式失效。

看来你不明白引用的概念

当你有这个:

BigDecimal incoming = new BigDecimal(2);
addTwo(incoming);
...

public BigDecimal addTwo(BigDecimal bigDecimal) {
  bigDecimal = bigDecimal.add(new BigDecimal(2));
}

事情是:incoming 在该方法结束后仍然是 2

那个 add() 调用创建了一个 new BigDecimal 对象,然后变量 bigDecimal 指向那个新对象。

但是引用incoming仍然指向旧对象!

长话短说:方法参数的关键字 final 根本 没有您想象的那样。它的唯一目的是防止你re-assigning到那个parameter/value。所以你真的很想了解 objects and references. And then about pass by reference.

之间的区别

示例 2 很好,但您根本不需要局部变量,我认为它们会降低方法的可读性。我会简单地做

public BigDecimal addTwo(final BigDecimal bigDecimal) {
    return bigDecimal.add(BigDecimal.valueOf(2));
}

What if I want to add another BigDecimal"? I have to return bigDecimal.add(BigDecimal.valueOf(2).add(BigDecimal.valueOf(3))? I think it's not so readable.

如果您要多次添加两个(或多个常量),那么提取常量作为常量可能是有意义的。喜欢,

private static final BigDecimal TWO = BigDecimal.valueOf(2);
private static final BigDecimal THREE = BigDecimal.valueOf(3);

public static BigDecimal addTwoAndThree(final BigDecimal bigDecimal) {
    return TWO.add(THREE).add(bigDecimal);
}

好吧,我承认我对 final 关键字一无所知。我必须说我不明白它的用处:

  • 如果在方法内部重新赋值一个参数,final与否,对象不变,当然

  • 如果您改为修改非不可变对象,final 或不修改,对象也会在方法之外更改。

  • 似乎带有 final 的参数唯一能做的就是它不能仅在方法内重新分配。

我必须说我经常做这样的事情:

public List<MyBean> findByNation(String nation) {
    if (nation != null) {
        nation = nation.toUpperCase();
    } else {
        nation = "";
    }

    [...]
}

public List<Date> findConfirmedDates(Date start, Date end) {
    if (end == null) {
        end = new Date();
    }
}

public String findFilteredSql(String queryFilter, SearchBean searchBean) {
    if (searchBean.getSupplierId() != null) {
        queryFilter += " JOIN SUPPLIER_COMMODITY sc ON sc.SUPPLIERID = v.SUPPLIERID ";
    }

    [...]
}

public static String format(BigDecimal val, Locale locale, int scale) {
    if (val == null) {
        return "-";
    }

    val = valLocal.subtract(BigDecimal.ONE);
    val = val.multiply(NumberUtility.BigDecimal100);

    [...]
}

是的,在大学里我了解到,如果我创建一个新变量,而不是覆盖旧变量,程序的速度会提高(我记不太清楚了,在我看来 CPU 和寄存器以某种方式参与......)。无论如何,这是 micro-optimizations,对进行数字计算的 C 或 Fortran 库很有用。

恕我直言,我编写上述方法的方式更具可读性,对不起 final

所以我没有发现 final 在这些情况下的实用性。而且,我根本不觉得它有用。我想我会采用问题中的示例 #1,我会简单地禁用 Eclipse 的警告,并且不添加这个可以说有用的关键字。