Java: Pourquoi cette méthode a-t-elle des effets secondaires?
-
05-07-2019 - |
Question
J'ai une méthode qui produit des effets secondaires, même si certaines variables sont marquées final
. Pourquoi est-ce? Peut-être suis-je confus à propos de ce que final
fait.
@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;
}
La solution
Le modificateur final
signifie uniquement que la référence ne peut pas être réaffectée. Cela n'empêche pas que l'état de l'objet soit modifié.
MODIFIER: juste pour Tom:
public void doSomething1(Object arg)
{
arg = new Object(); // OK.
}
public void doSomething2(final Object arg)
{
arg = new Object(); // Compile error.
}
Dans les deux cas, vous pouvez invoquer des méthodes sur l'objet pointé par arg
, y compris des méthodes qui modifient son état.
Autres conseils
Dan a la bonne réponse en finale. Ce que vous recherchez ressemble davantage à const en C ++, ce que Java n’a pas. Vous pouvez le simuler en procédant comme suit:
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;
}
}
puis faites:
void bar(final Foo foo)
{
foo.setX(5); // will not compile
}
void bar(final MutableFoo foo)
{
foo.setX(5); // will compile
}
Pas joli, mais ça marche. L'astuce consiste à s'assurer qu'aucune des méthodes de la classe parent (Foo) n'apporte de modifications aux variables d'instance - seul MutableFoo peut avoir des méthodes permettant à l'état de changer.
Bien sûr, la meilleure chose à faire est, autant que possible, d’écrire des classes immuables (rendre toutes les variables définitives) et d’appeler des méthodes sur des variables instance / classe qui ont des effets secondaires, pour que les choses ne changent pas