A peer at work has extended LinkedHashMap, and overridden removeEldestEntry similar to:
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class CompileTest {
static class MyMap<K, V> extends LinkedHashMap<K, V> {
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return true;
}
}
}
Notice the parameter class is Entry, not Map.Entry.
Eclipse generated the method signature. IntelliJ shows an error complains that Entry has private access in java.util.LinkedHashMap, and prefers Map.Entry. But it still compiles either way.
I wrote a smaller example to experiment:
public class CompileTest {
static class A{
public class Inner {
}
public void doStuff(Inner a){}
}
static class B extends A{
private class Inner {
}
}
static class C extends B {
public void doStuff(Inner a) { }
}
}
Now IntelliJ does not show an error, but the class fails to compile. Here are 2 situations that seem the same, where both the IDE and the compiler seem to alternate behaviors and also never agree with each other.
Can someone explain this?
From the Java Language Specification – 8.5 Member Type Declarations
We can deduce that:
B.InnerhidesA.Inner.Bdoes not inheritA.Inner.A.Inneris not a member(8.2) type ofB.Ccannot inheritA.InnerfromB. C cannot inheritB.InnerfromBbecause it’s private.Therefore
Cdoes not have a member typeInner. Assume there is no otherInnertype in C’s enclosing scopes (outer class; compilation unit; package), then type nameInnercannot be resolved.javactries to report a more detailed error, but that’s only a guess at your intention. An even better error message probably should include all the above explanations.In the 1st example about
Entry, the import statement declaresEntryin the entire compilation unit scope(i.e. the java file), thereforeEntryis resolved to beMap.EntryIntelliJ 10.5 doesn’t complain about the 1st example; apparently the bug has been fixed. It is still wrong on the 2nd example.
There is something funny about
private. Why does the spec explicitly excludeprivatemembers while it already requires members be “accessible”? ActuallyB.Inneris accessible in the entire body ofCompileTest(6.6.1), includingC.Ccan have adoStuff(B.Inner)and it will compile.That’s probably why IntelliJ screws up on the 2nd example; it thinks that
B.Inneris accessible toC, thereforeCinheritsB.Inner. It overlooked the extra exclusion clause ofprivatemembers in inheritance.This reveals 2 conflicting views on the scope of a
privatedeclaration. View #1 wants the scope to be the immediate enclosing class, view #2 wants it to be the top level enclosing class. #2 is less known by programmers, they’d be surprised to learn thatCcan accessB.Inner. #2 can be justified in the following way: they are all in the same file anyway, so there’s no need for encapsulation, we aren’t protecting anyone by prohibiting the access; allowing access is more convenient in most use cases.View #2 probably also argued that
Cshould inheritB.Inner– what could go wrong? Interestingly #2 wins on the general access control rule, but lost on the inheritance rule.The spec is really really messy, fortunately we usually don’t run into these complicated situations. It is rather the fault of
LinkedHashMapandHashMapthat repurpose a public name for private use.