为什么String类是final的?

7

可能是重复问题:
为什么Java中的String是final的?

在我的编程生涯中,有很多时候我希望String类不是final/sealed/NotInheritable的。

编程语言的设计者试图防止我做什么会使程序出现问题呢?

相反地,限制我扩展String类的目的是为了防止语言设计者所希望避免的错误。

您能列出可扩展字符串类的优缺点吗?


我意识到还有其他像数字这样被封装为final的类。我们可以先分析String,但您的评论不必局限于String。 - Blessed Geek
+1...我也曾经想过在我的生命中扩展String类,尽管我不记得为什么。 - functional
正如您提到的Java(标签),这个问题之前已经被问过了:https://dev59.com/gXI95IYBdhLWcg3w-DH0 - Kevin Brock
这是一个关于Java中为什么String是final的问题的精确重复。 - hvgotcodes
1
因为重复的问题已经有答案了,所以被投下了票。请参考这个链接:https://dev59.com/gXI95IYBdhLWcg3w-DH0 - Alain O'Dea
2个回答

5

String是一个不可变类,这意味着你在创建后无法修改它的状态。如果在将字符串传递到其他库或Map等地方后对其进行修改,结果将是不可预测的。

Java API的一个错误是BigIntegerBigDecimal不是final,这意味着当从不受信任的代码接收它们时需要执行防御性复制。相反,您始终可以相信一个String将保持一致。

不受信任的BigInteger:

public class DestructiveBigInteger extends BigInteger {

    public DestructiveBigInteger(String value) {
        super(value);
    }

    public BigInteger add(BigInteger val) {
        return BigInteger.ONE; // add() method does not behave correctly
    }

    public BigInteger subtract(BigInteger val) {
        throw new UnsupportedOperationException("subtract is broken");
    }
}

对于String类型的对象,不可能实现同样的操作。如Effective Java中所述,您需要制作这些类型对象的防御性副本:

public void setValue(BigInteger value) {
    this.value = new BigInteger(value.toByteArray());
}

1
确保你知道 final 关键字和 sealed 关键字的区别。标记为 final 的类也是 effectively sealed,但这不是 final 关键字的真正重点。final 类是 immutable 的,意味着它在创建后不能被修改。不可变类必须也是 sealed,否则可以继承、覆盖并添加一些非不可变行为。在Java中,字符串是不可变的,因为可变的字符串是 危险的,而且比它们值得的麻烦更多。大多数现代语言(Python,Javascript,C#,Java)都使用不可变的字符串。 - Ender

4

字符串是不可变数据类型,因此它是final的。如果盲目扩展String,将会导致很多库依赖于String对象的不可变性而出现严重问题。

将String扩展为可变类型对于String经过的任何代码都是不可见的,但会产生非常令人惊讶和恶劣的副作用,例如突然无法从HashMap中加载值,即使你已经有了键的String,因为hashCode已经被劫持了。


如果我想要使用字符串作为映射的键,我会使用String。如果我想要使用扩展的字符串作为映射的键,那就是我想要做的 - 防止它被基本字符串对象访问。对于我来说,不可变性的论点似乎站不住脚。 - Blessed Geek
但一个扩展的字符串成为一个字符串。因此,您可以将其用作从字符串到映射的键。无法派生并不妨碍您编写一个扩展字符串功能(使用组合)的类。它只是阻止您将该类的实例转换为字符串。 - jwg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接