Pregunta

Tengo un método que produce efectos secundarios, aunque ciertas variables están marcadas como final . ¿Por qué es esto? Quizás estoy confundido sobre lo que hace final .

@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;
}
¿Fue útil?

Solución

El modificador final solo significa que la referencia no se puede reasignar. No impide que se modifique el estado del objeto.

EDITAR: Solo para Tom:

public void doSomething1(Object arg)
{
    arg = new Object(); // OK.
}

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

En ambos casos, puede invocar métodos en el objeto al que apunta arg , incluidos los métodos que modifican su estado.

Otros consejos

Dan tiene la respuesta correcta en la final. Lo que buscas es más como const en C ++, que Java no tiene. Puedes simularlo haciendo esto:

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;
    }
}

entonces haz:

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

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

No es bonito, pero funciona. El truco es asegurarse de que ninguno de los métodos de la clase principal (Foo) realice cambios en las variables de instancia; solo MutableFoo puede tener métodos que permitan cambiar el estado.

Por supuesto, lo mejor que se puede hacer, en la medida de lo posible, es escribir clases inmutables (hacer que todas las variables sean definitivas) y no llamar a métodos en variables de instancia / clase que tienen efectos secundarios, para que las cosas no puedan cambiar

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top