在Clojure Programming(OReilly)中,有一个示例,其中java.io.BufferedWriter和java.io.Printwriter都放在代理中(每个代理一个代理)。然后将这些内容写入代理操作中。该书说,在代理行动中执行io是安全的。据我所知,在代理行动中所有副作用操作都可以。这是因为提交内的代理操作仅在提交成功时才会运行。其他代理操作中的代理操作仅在外部代理操作成功完成后运行。一般情况下,代理操作可以保证连续应用。
Clojure documentation说:“代理人的状态应该是不可改变的......”。
据我了解,atom和refs必须保存不可变值的原因是clojure可以回滚并重试多次提交。
我不明白的是:
1:如果Clojure确保代理操作只运行一次,为什么代理值必须是不可变的。 (例如,如果我在代理中持有一个java数组,并在代理操作中添加它,这应该没问题,因为该操作只会运行一次。这与向BufferedWriter添加行非常相似)
2:java.io.BufferedWriter被认为是不可变的吗?我知道你可以有一个稳定的引用,但如果代理操作在它上面执行io,它是否仍然被认为是不可变的?
3:如果BufferedWriter被认为是不可变的,我如何判断其他类似的java类是否是不可变的?
答案 0 :(得分:4)
我认为:
代理人持有的价值应该是“有效不可变的”(从JCIP借来的术语),因为它们在概念上应始终与自己相等。
这意味着,如果我.clone()
一个对象并比较两个副本,original.equals(copy)
应该是真的,无论我做什么(以及何时)。
从这个意义上说,面对可变性,不能保证充满getter / setter的典型Employee
类的实例等于它自己:equals()
将被定义为field-by - 场比较,所以测试可能会失败。
我相信你是正确的,从STM的角度来看,代理价值的可变性不会造成太大的伤害。但它会打破Clojure的时间模型,你不能改变过去的等等。
在决定Java类是否是不可变的时候:如果不深入实现,就不可能。你不必过多关心这个。
我在Java-land中进行以下类型分类:
可变对象(严重地)代表值 - Employee
等。永远不要将它们包装在Clojure引用类型中。
表示值的不可变对象 - 它们的不变性反映在doc或命名约定(“EmployeeBuilder”)中。 可以安全地包装任何Clojure参考。
非托管集合类型 - ArrayList等。避免互操作目的。
托管引用/集合类型 - AtomicReference,阻塞队列...... 它们可以与Clojure一起使用,但可疑的是将它们包装在Clojure引用中。
'IO'类型 - BufferedWriter,Swing的东西......你不关心它们的可变性,因为它们根本不代表值 - 你只是想要它们的副作用。 在代理中保护它们以保证访问序列化可能是有意义的。
答案 1 :(得分:2)
代理值应该是不可变的,因为有人可以这样做:
(def my-agent (agent (BufferedWriter.)))
(.write @my-agent "Hello world")
这基本上是在不经过代理控制机制的情况下修改代理值(在本例中是编写器)。
是的,BufferedWriter
是可变的,因为写入它可以改变其内部状态。它就像一个指针或引用而不是value.