Java编译器是否允许对静态调用流敏感?

Java编译器是否允许对静态调用流敏感?,java,jls,Java,Jls,以下是JLS第8.4.8.2节中的一个简短示例 class Super { static String greeting() { return "Goodnight"; } String name() { return "Richard"; } } class Sub extends Super { static String greeting() { return "Hello"; } String name() { return "Dick"; } } cla

以下是JLS第8.4.8.2节中的一个简短示例

class Super {
    static String greeting() { return "Goodnight"; }
    String name() { return "Richard"; }
}
class Sub extends Super {
    static String greeting() { return "Hello"; }
    String name() { return "Dick"; }
}
class Test {
    public static void main(String[] args) {
        Super s = new Sub();
        System.out.println(s.greeting() + ", " + s.name());
    }
}
根据对示例的讨论,运行
main()
的输出将是“晚安,迪克”。这是因为静态方法是根据调用它们的变量/表达式的静态类型来调用的


这里是我的问题:任何一个对流敏感度适中的编译器都可能发现,调用时存储在
s
中的任何对象的类型必须始终是
Sub
,因此如果允许编译器使用这些信息,即使调用静态方法也可能会有一些动态绑定的感觉。为什么这是不允许的?Java是否有明确的目标,即每个编译器生成行为完全相同的字节码,还是有其他原因?

事实上,这里的
s.greeting()
等同于
Super.greeting()
,因为
s
被定义为
Super
,静态方法不关心类实例。正如你肯定知道的,他们是全班的。因此,直接从类实例调用静态方法是没有意义的。当然,实例
s
是您指定的
Sub()
,因此调用了非静态方法
Sub.name()

发件人:

还可以使用对象引用引用静态字段,如

myBike.numberOfBicycles

但这是令人沮丧的,因为它没有明确表明他们 是类变量


允许静态方法以类实例的方式运行只会使代码可读性降低,更加晦涩难懂,更难调试,而不会真正添加任何有用的功能。

它不是太特定于java的。想象一下,您使用
s.getClass().greeting()
进行“智能”编译:

class A extends Sub {
    static String greeting() { return "Allo"; }
}
class B extends Sub {
    static String greeting() { return "Brrr"; }
}
Super s = condition? new A() : new B();
assert s.greeting.equals(Sub.greeting()); // If compiler cannot infer condition.
编译器应该跨多个源执行此操作吗?库外,源代码可能不可用

我认为java中的谬论是允许
s.greeting()


由于静态继承没有任何用处,最好不要发明这样的特性。

当然,JLS会注意只有一种Java语义。静态成员不受子类型规则的约束。这就是全部。就我而言,允许在表达式上调用它们而不是只调用相应的类型是一个语言缺陷。Java的设计者和维护者已经尽了很大的努力(几乎是一个错误),以确保所有版本的Java在所有环境中的行为完全相同。在
invokestatic
的情况下,字节码只接受一个类和方法——使用类的实例变量的能力对编译器来说是一种“语法糖”。是的。我能做到这一点。我的问题更像是“因为允许对表达式调用静态方法(此处
s
),为什么不使用已知的最具体的静态类型来代替声明的类型呢?但我想如果Java坚持独特的语义,那就是答案。事实上,你不应该使用实例来调用静态方法。你所说的这种模糊性只会使代码可读性降低,语言更加晦涩但实际上没有添加任何有用的功能。这就是为什么在非静态上下文中调用静态方法时会收到警告。不应该这样调用它。更不用说,静态方法在编译时绑定,而作为实例的方法可以在编译时或运行时绑定。