Search code examples
javafinaltestcaseside-effectsjgrapht

Java: Why does this method have side effects?


I have a method that is producing side effects, even though certain variables are marked final. Why is this? Perhaps I am confused about what final does.

@Test
public void testSubGraph() {
    WeightedGraph<String, DefaultWeightedEdge> g = generateSimpleCaseGraph();

    Graph<String, DefaultWeightedEdge> sub = ChooseRoot.subgraphInDirection(g, "alpha", "l");

    assertEquals(g, generateSimpleCaseGraph()); //fails 
}

public static <V, E extends DefaultEdge> Graph<V, E> subgraphInDirection(final Graph<V, E> g, final V start, final V sink) {
    Graph<V, E> sub = removeEdges(g, start, sink);
    return removeUnconnectedNodes(sub, start);
}

private static <Vertex, Edge extends DefaultEdge> Graph<Vertex, Edge> removeEdges(final Graph<Vertex, Edge> g, Vertex start, Vertex sink) {
    final Set<Edge> outEdges = new HashSet<Edge>(g.edgesOf(start));
    boolean removedEdge;

    for (Edge e : outEdges) {
        if (! (g.getEdgeTarget(e).equals(sink) || g.getEdgeSource(e).equals(sink))) {
            removedEdge = g.removeEdge(e);
            assert removedEdge;
        }
    }
    return g;
}

private static <Vertex, Edge> Graph<Vertex, Edge> removeUnconnectedNodes(Graph<Vertex, Edge> g, Vertex start) {
    ConnectivityInspector<Vertex, Edge> conn = new ConnectivityInspector<Vertex, Edge>((UndirectedGraph<Vertex, Edge>) g);
    boolean removedVertex;

    final Set<Vertex> nodes = new HashSet<Vertex>(g.vertexSet());
    for (Vertex v : nodes) {
        if (! conn.pathExists(start, v)) {
            removedVertex = g.removeVertex(v);
            assert removedVertex;
        }
    }
    return g;
}

Solution

  • The final modifier only means that the reference cannot be reassigned. It does not prevent the object's state from being modified.

    EDIT: Just for Tom:

    public void doSomething1(Object arg)
    {
        arg = new Object(); // OK.
    }
    
    public void doSomething2(final Object arg)
    {
        arg = new Object(); // Compile error.
    }
    

    In both cases you can invoke methods on the object pointed to by arg, including methods that modify its state.