工作中的同伴扩展了LinkedHashMap,并覆盖了removeEldestEntry
,类似于:
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;
}
}
}
请注意,参数类是Entry
,而不是Map.Entry
Eclipse生成了方法签名。 IntelliJ显示错误,抱怨Entry
在java.util.LinkedHashMap
中拥有私人访问权限,并且更喜欢Map.Entry
。但它仍然可以编译。
我写了一个较小的例子来试验:
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) { }
}
}
现在IntelliJ没有显示错误,但是类无法编译。以下是两种看起来相同的情况,其中IDE和编译器似乎都是交替行为,也从不同意彼此。
有人可以解释一下吗?
答案 0 :(得分:4)
来自Java语言规范 - 8.5 Member Type Declarations
如果类声明了成员类型 用一定的名字,然后 据说这种类型的声明 隐藏任何和所有可访问的 使用。声明成员类型 超类中的同名和 班级的超级接口。
一个类继承自它的直接 超类和直接超接口 所有非私人会员类型 超类和超级接口 这两个代码都可以访问 这个班并没有被一个人隐藏 在课堂上宣言。
我们可以推断出:
B.Inner
隐藏A.Inner
。 B
不会继承A.Inner
。 A.Inner
不是B
的成员(8.2)类型。 C
无法从A.Inner
继承B
。 C无法从B.Inner
继承B
,因为它是私有的。
因此C
没有成员类型Inner
。假设C的封闭范围(外部类;编译单元;包)中没有其他Inner
类型,则无法解析类型名称Inner
。
javac
尝试报告更详细的错误,但这只是对您的意图的猜测。更好的错误消息可能应包括上述所有解释。
在关于Entry
的第一个示例中,import语句在整个编译单元范围(即java文件)中声明Entry
,因此Entry
被解析为Map.Entry
}
IntelliJ 10.5并没有抱怨第一个例子;显然这个bug已被修复。第二个例子仍然是错误的。
private
有一些有趣的东西。为什么规范明确排除private
成员,而已经要求成员“可访问”?实际上,B.Inner
(6.6.1)可以访问CompileTest
,包括C
。 C
可以有一个doStuff(B.Inner)
,它会编译。
这可能就是为什么IntelliJ搞砸了第二个例子;它认为B.Inner
可以访问C
,因此C
会继承B.Inner
。它忽略了继承中private
成员的额外排除条款。
这揭示了对private
声明范围的两个相互矛盾的观点。视图#1希望范围是直接封闭类,视图#2希望它是顶级封闭类。程序员不太了解#2,他们会惊讶地发现C
可以访问B.Inner
。 #2可以通过以下方式证明:它们都在同一个文件中,因此不需要封装,我们不会通过禁止访问来保护任何人;在大多数用例中允许访问更方便。
观点#2可能还认为C
应该继承B.Inner
- 可能出现什么问题?有趣的是,#2在通用访问控制规则上获胜,但在继承规则上丢失了。
规范确实非常混乱,幸运的是我们通常不会遇到这些复杂的情况。而LinkedHashMap
和HashMap
的错误则是将公共名称重新用于私人用途。
答案 1 :(得分:1)
这是因为Entry
和Map.Entry
是两个不同的类。第一个是默认范围,在HashMap中定义,后者是public interface。因此,您在具有更广范围的方法中使用私有参数。这确实可以编译,但可能不是你的意图。
答案 2 :(得分:0)
您只需指定要引用的“内部”。 IDE只是一个帮助您的工具;编译器始终是权威参考。
...
static class C extends B {
public void doStuff(A.Inner a) {
}
}
...
为了进一步说明,您可以使用标准变量获得相同的“细微/奇怪”行为。私有定义正在影响公共定义。
public class CompileTest {
static class A {
public static Object foo;
}
static class B extends A {
private static Object foo;
}
static class C extends B {
public void doStuff() {
foo = new Object(); // Will not compile - 'foo' is not visible
A.foo = new Object(); // Works
}
}
}