Java 不变性和图形模型-如何创建它们?
我将使用示例,因为它更容易显示我想要做什么。我有三个类X、Y和Z,我希望能够将它们作为不可变对象。给你 X类:Java 不变性和图形模型-如何创建它们?,java,data-structures,graph,architecture,immutability,Java,Data Structures,Graph,Architecture,Immutability,我将使用示例,因为它更容易显示我想要做什么。我有三个类X、Y和Z,我希望能够将它们作为不可变对象。给你 X类: public class X{ private int id; private String dataX; private Collection<Y> collectionOfY; private Collection<Z> collectionOfZ; /* Constructors */ /* Getter
public class X{
private int id;
private String dataX;
private Collection<Y> collectionOfY;
private Collection<Z> collectionOfZ;
/* Constructors */
/* Getters */
/* "with" functions */
}
公共类X{
私有int-id;
私有字符串dataX;
私人收藏;
私人收藏;
/*建设者*/
/*吸气剂*/
/*“带”功能*/
}
Y类:
public class Y{
private int id;
private String dataY;
private Collection<X> collectionOfX;
private Collection<Z> collectionOfZ;
/* Constructors */
/* Getters */
/* "with" functions */
}
公共类Y{
私有int-id;
私有字符串数据;
私人收藏外汇;
私人收藏;
/*建设者*/
/*吸气剂*/
/*“带”功能*/
}
Z类:
public class Z{
private int id;
private String dataZ;
private Collection<X> collectionOfX;
private Collection<Y> collectionOfY;
/* Constructors */
/* Getters */
/* "with" functions */
}
公共类Z{
私有int-id;
私有字符串dataZ;
私人收藏外汇;
私人收藏;
/*建设者*/
/*吸气剂*/
/*“带”功能*/
}
因此,它们相互联系,形成一个具有循环结构的图。如果可能的话,我想要不可变的对象。我已经实现了“With”函数,它将只更改一个属性就发回给定对象的副本
但是,如果我实现了完全不变性,我可能会导致(直接或间接)链接到我更改的对象的每个数据的复制:
更改“dataZ”通常意味着我想用链接X和Y中新创建的对象替换旧对象。由于更改等原因,这些对象将被复制
另一个解决方案是使链接这三个类的集合不是不可变的(并且只有部分不可变),但是我几乎失去了尝试具有不可变性的所有兴趣
有人有好主意吗?还是我在要求不可能的事情?我是否应该回到旧的setter(即使它意味着更复杂的并发管理)
提前感谢;) C#能够通过其内置的Lazy
解决这个问题。Lazy
是一个尚未计算其实际值的对象-在构造Lazy
时,将工厂函数作为参数传递
您可以使用value
属性询问Lazy
的值。如果工厂函数已运行,则返回已构造的值。如果factory函数尚未运行,则此时它将运行。无论何种情况,值
属性的返回总是相同的。一旦确定了一个特定的惰性值,它就会保持不变
lazy是对可能尚未构造的对象的不可变引用。它在内部使用易变性,但从外部看,它似乎是不可变的
在您的例子中,如果您有一个相当于Lazy
的Java,那么您可以将您的集合从Collection
更改为Collection
。这将使X的实例能够引用一些尚未构造的Y的实例。而构造X
s、Y
s和Z
s的代码不会直接构建这些实例,而是构建Lazy
的实例。这些实例将工厂函数作为参数;反过来,这些工厂函数需要引用一些Lazy
值。这意味着,在构建和连接这些东西的函数的上下文中,需要对惰性实例进行可变引用
为了理解我的意思,如果您尝试创建一个由两个对象组成的循环(我不完全熟悉Java 8,所以可能会有语法错误):
a;
懒惰的b;
a=新懒惰(()->{
List ys=新的ArrayList();
ys.add(b.getValue());
返回新的X(ys);
});
b=新的惰性(()->{
List xs=new ArrayList();
添加(a.getValue());
返回新的Y(xs);
});
实际上,我认为这是行不通的。我认为闭包变量在Java中必须是最终变量(在C#中并非如此)。所以我认为你需要这样做:
final Lazy<X>[] a = new Lazy<X>[1];
final Lazy<Y>[] b = new Lazy<Y>[1];
a[0] = new Lazy<X>(() -> {
List<Y> ys = new ArrayList<Y>();
ys.add(b[0].getValue());
return new X(ys);
});
b[0] = new Lazy<Y>(() -> {
List<X> xs = new ArrayList<X>();
xs.add(a[0].getValue());
return new Y(xs);
});
final Lazy[]a=新的Lazy[1];
final Lazy[]b=新Lazy[1];
a[0]=新的惰性(()->{
List ys=新的ArrayList();
添加(b[0].getValue());
返回新的X(ys);
});
b[0]=新的延迟(()->{
List xs=new ArrayList();
添加(a[0].getValue());
返回新的Y(xs);
});
这是因为两个lambda没有立即进行评估。只要在执行这些lambda之前将a[0]
和b[0]
设置为有效值,一切都会正常工作
注意,这使用了可变性,但在非常有限的范围内使用了可变性。Lazy
实例中存在可变性,但这些实例似乎是不可变的。wire-up函数具有易变性,但该函数将提前运行并终止,而构造的对象可以存活更长的时间。至少对我来说,这种受约束的易变性是一种可以接受的权衡。所以你想让任何人都不能修改对象吗?或者可以创建但无法修改属性?
final Lazy<X>[] a = new Lazy<X>[1];
final Lazy<Y>[] b = new Lazy<Y>[1];
a[0] = new Lazy<X>(() -> {
List<Y> ys = new ArrayList<Y>();
ys.add(b[0].getValue());
return new X(ys);
});
b[0] = new Lazy<Y>(() -> {
List<X> xs = new ArrayList<X>();
xs.add(a[0].getValue());
return new Y(xs);
});