views:

278

answers:

2

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;
}
+12  A: 

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 both cases you can invoke methods on the object pointed to by arg, including methods that modify its state.

Dan Dyer
I was going to essentially post the same thing, but give an example... how about you just add an example to yours :-).
Tom
There you go :)
Dan Dyer
ok, so how can I avoid changing the object's state? Do I have to clone it?
Rosarch
You avoid changing the object state, by, well, not changing the object state (the only problem is that compiler won't help you detect that). Naturally, _deep_ cloning the object will let you change the resulting copy with impunity, since you alone own it.
Pavel Minaev
In the first case, there will no side effect. Whatever you do with arg after line 1 will stay inside the method. After doSomething1 finishes arg will be unchanged.
fastcodejava
@fastcodejava: Yes, perhaps my example made it less clear. The code is meant to demonstrate what final does. The comment about being able to modify the object's state assumes that you haven't reassigned the reference.
Dan Dyer
@Dan It is fine, no fighting. I thought I will point out that your example ended up implying something you did not probably mean to.
fastcodejava
+1  A: 

Dan has the right answer on final. What you are after is more like const in C++, which Java does not have. You can simulate it by doing this:

public class Foo
{
    protected int x;

    public Foo(final int val)
    {
        x = val;
    }

    public int getX()
    {
        return (x);
    }
}

public class MutableFoo 
    extends Foo
{
    public MutableFoo(final int val)
    {
        super(val);
    }

    public void setX(final int val)
    {
        x = val;
    }
}

then do:

void bar(final Foo foo)
{
    foo.setX(5); // will not compile
}

void bar(final MutableFoo foo)
{
    foo.setX(5); // will compile
}

Not pretty, but it works. The trick is to make sure that none of the methods in the parent class (Foo) make any changes to the instance variables - only MutableFoo can have methods that allow the state to change.

Of course the best thing to do, as much as possible, is to write immutable classes (make all the variables final) and do not call methods on instance/class variables that have side effects, so that things cannot change

TofuBeer
+1 on creating immutable classes, but the way to do it is not only having al variables final but also not having mutable attributes or never calling mutating methods on them nor returning references to mutable attributes.
David Rodríguez - dribeas
good point on immutability - I'l update my answer.
TofuBeer