为什么Java中的包装类是最终的?

时间:2015-01-30 14:01:45

标签: java

就像听起来一样。我需要扩展他们的功能,我不能。为什么他们被定义为最终?

示例:

class MyInteger extends Integer
{

}

5 个答案:

答案 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没有坚持Integerfinal,则不会有这样的保证,因此代码必须进行深层复制。否则,调用者可以通过在列表中传递有效的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

}

如果有人因类似的问题而无意中把它留在这里:)

相关问题