为什么原始数据类型的包装类没有setter方法?

4
为什么包装类(如Integer、Double等)没有设置其内部基本类型值的setter方法?
我之所以提出这个问题,是因为这种功能将简化计算,并使Java语言更加灵活。
让我举几个例子。
1)我们来看以下示例:
Integer x = new Integer(5);
x++;

上述代码背后正在执行自动装箱。类似于:

int x_tmp = x.intValue();
x_tmp++;
x = new Integer(x_tmp); // Yes that's a new memory allocation

由于这个问题,在Wrapper上进行微积分比在普通的原始类型上执行要慢。使用setter可以更容易地增加内部值,而不需要在堆上分配另一个对象。
2)另一个困扰我的问题是,在Java中无法编写像C(使用指针)或C ++(指针或引用)中那样的交换函数。
如果我写void swap(Integer x, Integer y),我无法访问内部值,因此我将无法交换值。
PS: 我的一个朋友建议我考虑更大的画面,并考虑并发性和类型不可变性。
所以你有解释吗? 谢谢!
4个回答

6

除非你需要将它们放入集合中,否则通常不会使用包装类。如果它们是可变的,则在集合中使用和用作哈希表键时会造成问题。

集合和哈希表需要哈希值始终保持相同。


我猜这应该是程序员的责任,而不是语言的责任。但是嘿,我刚开始以更有组织的方式学习Java。 - Andrei Ciobanu
不仅仅是包装类:我们曾经遇到过一些使用非平凡类作为缓存键的问题。这些类是可变的(更糟糕的是,只有默认构造函数),并且确实在某个时候,有人(也就是我)通过更改它们的值来重用了一些键。修复后的bug包括使这些关键类成为不可变的... - Rodney Gitzel

4

1) 使用setter,封装类型将是可变的。在许多方面,不可变性都是一件好事...例如,线程、代码的一般可理解性等等。个人认为,例如CalendarDate是可变的,这是一件遗憾的事情。

事实上,你对x++;的扩展并不完全正确——它使用了Integer.valueOf, 它并不总是创建新值。例如:

Integer x = 5;
x++;
Integer y = 5;
y++;

// This prints true    
System.out.println(x == y); // Compare references

只有一部分Integer值会被缓存(规范定义了哪些值必须这样处理,但如果JRE希望可以允许更多的值)...但这意味着它不会总是创建新对象。

2) 是的,Java没有按引用传递。坦白说,我很少发现这是个问题。你真的需要多经常交换变量的值吗?


这不仅仅是关于交换的问题。还有其他情况,当我认为某个问题在某种程度上存在时,然后我意识到我必须重新思考这个问题。 - Andrei Ciobanu
@Andrei:通常我发现这鼓励我将相关值封装在一个单独的类型中,这本来就是个好主意。你自己多频繁地需要它呢? - Jon Skeet
1
那么不可变性只是为了保护“程序员”并简化他/她的工作吗? - Andrei Ciobanu
@Andrei:有点像封装、垃圾回收、多态等,都是为了让程序员的生活更轻松。根据我的经验,在很多情况下这是一个巨大的优势。 - Jon Skeet

4

缓存-128到127范围内的整数需要使用不可变的整数。考虑以下代码:

Integer id = Integer.valueOf(1);  // a new Integer, cached in Integer class

// and somewhere else

Integer key = Integer.valueOf(1);  // returns the cached value

现在,如果整数是可变的,并且有一个setter,如果有人这样做了:
key.setValue(2);  // not legal Java code, just for demonstration

这会改变id的值,并且许多人会感到惊讶:
Integer one = Integer.valueOf(1);
if (one != 1)
   System.out.println("Surprise! I know, you expected `1`, but ...");

1

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