就像听起来一样。我需要扩展他们的功能,我不能。为什么他们被定义为最终?
示例:
class MyInteger extends Integer
{
}
答案 0 :(得分:17)
有充分的理由使原始包装器最终成功。
首先,请注意这些类在语言本身中获得特殊处理 - 与任何普通类不同,编译器会识别这些类以实现自动(非)装箱。简单地允许子类就会产生陷阱(展开子类,执行算术和回退会改变类型)。
此外,包装类型可以具有共享实例(例如,查看Integer.valueOf(int)),要求包装器的实例严格不可变。允许子类会打开一堆无法再保证不可变性的蠕虫,迫使强大的代码被编写,好像包装器是可变的,导致防御性拷贝,浪费内存和CPU时间,并且还会产生问题,因为它们在多个方面有用。线程场景。
本质:包装器类型需要是不可变的,以确保所有实例的统一功能;不可变性是他们已知财产的重要组成部分。为了保证不变性,类型需要才是最终的。
如果您需要添加功能,请将其实现为实用程序类,就像对原语一样(例如,参见java.lang.Math)。
编辑:为了解决为“不需要的课程”所做的案例,必须最终确保不变性。严格来说,这是事实,包装可以设计为非最终类,只使所有方法最终。这将缓解大部分陷阱,但它会导致另一个问题:与新Java版本的兼容性。想象一下,你决定用一种新方法创建自己的子类型MyInteger,例如: “整数减量()”。一切正常,直到......在Java 20中,语言设计者决定向API添加一个减量()方法(如上所述,它将是 final )。 Bam,你的班级不再加载,因为它试图覆盖最后的方法。
答案 1 :(得分:5)
这是一个安全功能,它允许库在集合中使用字符串和包装器,或者不使用“防御副本”。
如果允许用户从Integer
派生自己的值,他们可以制作一个可变版本。然后他们可以将此Integer
的实例传递给期望收集Integer
的库,如下所示:
class ApiClass {
private final List<Integer> intList;
ApiClass (List<Integer> ints) {
// Make a defensive copy
intList = new ArrayList<Integer>(ints);
// Go through the list, and check the values
...
}
}
目前构造函数应该对集合进行防御性复制,但是浅拷贝就足够了,因为Integer
里面的内容不能改变。但是,如果Java没有坚持Integer
为final
,则不会有这样的保证,因此代码必须进行深层复制。否则,调用者可以通过在列表中传递有效的ApiClass
来构造Integer
,然后浏览MyInteger
的列表,并将其值更改为构造函数认为无效的值。这将打破ApiClass
内其他方法的假设,可能导致崩溃或暴露“普通”Integer
不会泄漏的信息。
答案 2 :(得分:2)
他们需要不变。正确实现不变性的一个关键特性是它无法扩展。如果你可以扩展你可以这样做:
class MyInteger extends Integer
{
private int myOverrideValue;
@Override
public int intValue() {
return myOverrideValue;
}
public void setIntValue(int value) {
myOverrideValue = value;
}
}
现在,我可以将可变类型MyInteger
的对象传递给期望intValue
不会更改的类和代码。
答案 3 :(得分:1)
它可以更改Wrapper类中方法的行为。
假设您有一个Employee
类,该类具有方法greet
。调用greet
方法时,它只打印Hello everyone!
。这就是greet
方法的 预期行为
public class Employee {
void greet() {
System.out.println("Hello everyone!");
}
}
现在,让GrumpyEmployee
成为Employee
的子类,并覆盖greet
方法,如下所示。
public class GrumpyEmployee extends Employee {
@Override
void greet() {
System.out.println("Get lost!");
}
}
现在在下面的代码中查看sayHello
方法。它以Employee
实例为参数,并调用greet方法,希望它表示 Hello everyone!
,但是我们得到的是 Get lost!
。这种行为上的变化是由于Employee grumpyEmployee = new GrumpyEmployee();
public class TestFinal {
static Employee grumpyEmployee = new GrumpyEmployee();
public static void main(String[] args) {
TestFinal testFinal = new TestFinal();
testFinal.sayHello(grumpyEmployee);
}
private void sayHello(Employee employee) {
employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
}
}
如果将Employee
类设为final
,则可以 避免 这种情况。现在,您可以想象,如果Wrapper
类未声明为final
,那么厚脸皮的程序员可能会造成多大的混乱。
答案 4 :(得分:0)
对此表示抱歉,五年后激活了该线程,但是我看不到任何解决方案的建设性建议-为什么不简单地封装它呢?
public MyInteger {
private Integer value;
public MyInteger(Integer value) {
this.value = value;
}
//... Add your custom math methods here
}
如果有人因类似的问题而无意中把它留在这里:)