我有一个Path类,我认为它是不可变的。在另一个名为Test的类中,我有一个对象的最终引用。
然而,在构造函数和getter方法之间,即使Path对象是不可变的并且引用是final,Path对象也会更改。我知道这一点,因为Path中int数组节点的长度从构造函数变为getter。看起来这个对象完全是一个完全不同的对象。
我的程序是多线程的,但是我用单个线程尝试过它并且没有解决问题。
这是不可变的Path类
public class Path implements Iterable<Point> {
private final int[] nodes;
private final double distance;
public Path(Scenario scenario, int gateway, int sensor){
this.scenario = scenario;
nodes = new int[2];
nodes[1] = -gateway - 1;
nodes[0] = sensor;
distance = scenario.DISTANCE_GATEWAY_SENSOR[gateway][sensor];
}
public Path(Path base, int newSensor){
scenario = base.scenario;
//Copy the old path. These are rigid structures so that we do not need to deep copy
nodes = new int[base.nodes.length + 1];
for(int i = 0; i < base.nodes.length; i++)
nodes[i + 1] = base.nodes[i];
nodes[0] = newSensor;
distance = base.distance + scenario.DISTANCE_SENSOR_SENSOR[newSensor][nodes[1]];
}
public Path(Scenario scenario, int[] nodes, boolean isSensor, double distance){
this.scenario = scenario;
this.distance = distance;
this.nodes = Arrays.copyOf(nodes, nodes.length);
if(!isSensor)
for(int i = 0; i < this.nodes.length; i++)
this.nodes[i] = -this.nodes[i] -1;
}
@Override
public Iterator<Point> iterator() {
return new PointIterator();
}
public class PointIterator implements Iterator<Point>{
private int next = -1;
@Override
public boolean hasNext() {
return next + 1 < nodes.length;
}
@Override
public Point next() {
int p = nodes[++next];
if(p >= 0)
return scenario.SENSOR_LOCATION[p];
return scenario.CS_LOCATION[-p - 1];
}
@Override
public void remove() {
throw new IllegalAccessError("This method is not supported");
}
}
}
这里是Test类(带有对Path类的最终引用)
public class Test {
private final Path gatewayTour;
public Test(Scenario scenario, boolean[] chosenGateway){
distanceFitness = 0;
Point current = scenario.SINK_LOCATION;
boolean visited[] = new boolean[scenario.CONFIG.NUM_CS];
int nextGateway;
LinkedList<Integer> order = new LinkedList<>();
do {
double minimumDistance = Double.MAX_VALUE;
nextGateway = -1;
for(int i = 0; i < scenario.CONFIG.NUM_CS; i++)
if(!visited[i] && CHOSEN_GATEWAYS[i] && scenario.CS_LOCATION[i].isCloserThan(minimumDistance, current)) {
nextGateway = i;
minimumDistance = scenario.CS_LOCATION[i].distance(current);
}
if(nextGateway >= 0) {
distanceFitness += minimumDistance;
visited[nextGateway] = true;
order.add(nextGateway);
current = scenario.CS_LOCATION[nextGateway];
}
} while(nextGateway >= 0);
int path[] = new int[order.size()];
Iterator<Integer> it = order.iterator();
for(int i = 0; i < order.size(); i++)
path[i] = it.next().intValue();
gatewayTour = new Path(scenario, path, false, distanceFitness);
}
public Path getGatewayTour(){
//Here, the gatewayTour object has changed and does not have the same content as in the constructor
return gatewayTour;
}
}
我的程序中是否有允许对象更改的内容?我会更精确:是否有任何东西允许在Path类中的int数组“节点”改变长度?因为这是真正的问题。
[编辑]:我的测试存在缺陷,这使我相信我的'nodes'数组的值发生了变化。感谢所有指出我的代码中存在缺陷或可能改进的人。
我会接受AlexR的答案,因为他指出可以改变最终数组中的各个元素;我不知道的东西,这有助于解决问题。
答案 0 :(得分:7)
Word final
表示无法更改标有此词的引用。这并不意味着无法更改引用的对象。
这意味着通过更改其字段来更改Path
的实例没有问题。是的,你是对的,你的领域也是最终的。但是让我们检查一下:
private final int[] nodes;
private final double distance;
private final Scenario scenario;
distance
是原语,因此在初始化期间分配后确实无法更改。 nodes
是一个数组,即对象。数组本身不能更改,即引用引用相同的数组。但是,您可以更改数组的元素。
scenario
也是对象。您尚未在此处发送类Scenario
,但如果可以更改此类的字段,则可以更改此对象。
答案 1 :(得分:2)
private final int[] nodes;
仍然是可变的,假设您的构造函数只是复制数组引用。
public Path(int[] nodes, double distance) {
this.node = nodes;
this.distance = distance;
}
这是因为Path
的{{1}}仍指向传入的实例。如果该实例发生更改,则nodes
的状态已更改。
一种解决方案是在构造函数中复制Path
(使用node
)。
答案 2 :(得分:0)
为了确保答案是正确的,我们需要查看更多代码;目前尚不清楚在哪里改变了什么。但是,如果想法是保证 nodes
是不可修改的,那么原始数组(final或not)将不起作用。更像是
private final List<Integer> nodes;
public Path(Integer[] array /* note boxed as Integer */) {
nodes = java.util.Collections.unmodifiableList(
java.util.Arrays.asList(array));
/* etc. */
}
答案 3 :(得分:0)
问题在这里!
public Path(Scenario scenario, int[] nodes, boolean isSensor, double distance){
this.scenario = scenario;
this.distance = distance;
this.nodes = nodes;
您复制节点数组引用。
使用:
this.nodes = Arrays.copy(nodes, 0, nodes.length);
如果修改数组,更改将反映到Path
!同样,如果在构造函数中修改数组,更改将反映给调用者...
因此,你的班级目前不是一成不变的。此外,“真实的”(据我所知)不可变类本身是final
。