Java: Access local variables from anon inner class? (PriorityQueue)
I want to use a PriorityQueue
to do a topological sort on a graph. For brevity, I'd like to use an anonymous inner class for the comparator. However, I need access to the graph g
in order to determine the in degree of the nodes I'm looking at. Is this possible?
/**
* topological sort
* @param g must be a dag
*/
public static Queue<String> topoSort(DirectedGraph<String, DefaultEdge> g) {
Queue<String> result = new PriorityQueue<String>(g.vertexSet().size(),
new Comparator<String>() {
DirectedGraph<String, DefaultEdge> g;
@Override
public int compare(String arg0, String arg1) {
if (g.inDegreeOf(arg0) < g.inDegreeOf(arg1)) {
return -1;
}
if (g开发者_JAVA技巧.inDegreeOf(arg0) > g.inDegreeOf(arg1)) {
return 1;
}
return 0;
}
});
result.addAll(g.vertexSet());
return result;
}
CORRECTED CODE
/**
* topological sort
* @param g must be a dag
*/
public static Queue<String> topoSort(final DirectedGraph<String, DefaultEdge> g) {
Queue<String> result = new PriorityQueue<String>(g.vertexSet().size(),
new Comparator<String>() {
@Override
public int compare(String arg0, String arg1) {
if (g.inDegreeOf(arg0) < g.inDegreeOf(arg1)) {
return -1;
}
if (g.inDegreeOf(arg0) > g.inDegreeOf(arg1)) {
return 1;
}
return 0;
}
});
result.addAll(g.vertexSet());
return result;
}
Yes, make it final:
public static Queue<String> topoSort(final DirectedGraph<String, DefaultEdge> g) {
See The Final Word On the final Keyword:
Anonymous Local Classes
The second situation involving final variables is actually mandated by language semantics. In that situation, the Java compiler won't let you use a variable unless it is declared final. This situation arises with closures, also known as anonymous local classes. Local classes can only reference local variables and parameters that are declared final.
public void doSomething(int i, int j) { final int n = i + j; // must be declared final Comparator comp = new Comparator() { public int compare(Object left, Object right) { return n; // return copy of a local variable } }; }
The reason for this restriction becomes apparent if we shed some light on how local classes are implemented. An anonymous local class can use local variables because the compiler automatically gives the class a private instance field to hold a copy of each local variable the class uses. The compiler also adds hidden parameters to each constructor to initialize these automatically created private fields. Thus, a local class does not actually access local variables, but merely its own private copies of them. The only way this can work correctly is if the local variables are declared final, so that they are guaranteed not to change. With this guarantee in place, the local class is assured that its internal copies of the variables accurately reflect the actual local variables.
If a local variable is used in an inner class, the compiler generates a instance field for the inner class and copies the local variables value to it. Now, if the local variable changes then the instancs variable value will still be having old value. So, in order to make the local variables value same as instance field and not mutable, it is made final.
FYI, with google collections you also have the option of
Ordering.natural().onResultOf(
new Function<String, Integer>() {
public Integer apply(String arg) {
return g.inDegreeOf(arg);
}
});
This doesn't necessarily save you a lot of typing, as you can see, unless you can find other uses for that Function, but it does shield you from the bugs that very often creep into your by-hand comparator implementations.
精彩评论