为什么不通过Java编译器的错误实例调用静态方法?
我相信你们都知道我的意思 – 代码如:
Thread thread = new Thread(); int activeCount = thread.activeCount();
引发编译器警告。 为什么不是一个错误?
编辑:
要清楚:问题与Threads无关。 我认识到在讨论这个时候通常会给出Thread的例子,因为它们可能会把事情搞得一团糟。 但真正的问题是,这种用法总是无稽之谈,你不能(胜任)写这样一个电话,并指出它。 这种types的方法调用的任何例子都是无聊的。 这是另一个:
String hello = "hello"; String number123AsString = hello.valueOf(123);
这看起来好像每个String实例都带有一个“String valueOf(int i)”方法。
基本上我相信Javadevise者在devise语言时犯了一个错误,由于涉及到兼容性问题,修复这个问题为时已晚。 是的,这可能会导致非常令人误解的代码。 是的,你应该避免它。 是的,你应该确保你的IDE被configuration为把它当作一个错误,IMO。 你应该自己devise一种语言,牢记这一点,作为一种避免的事情的例子:)
只是为了回应DJClayworth的观点,以下是C#中允许的内容:
public class Foo { public static void Bar() { } } public class Abc { public void Test() { // Static methods in the same class and base classes // (and outer classes) are available, with no // qualification Def(); // Static methods in other classes are available via // the class name Foo.Bar(); Abc abc = new Abc(); // This would *not* be legal. It being legal has no benefit, // and just allows misleading code // abc.Def(); } public static void Def() { } }
为什么我认为这是误导? 因为如果我看代码someVariable.SomeMethod()
我期望它使用someVariable
的值 。 如果SomeMethod()
是一个静态方法,那么期望是无效的; 代码欺骗了我。 这怎么可能是一件好事?
奇怪的是,Java不会让你使用一个潜在的未初始化variables来调用一个静态方法,尽pipe事实上它将使用的唯一信息是variables的声明types。 这是一个不协调和无益的混乱。 为什么允许它?
编辑:这个编辑是对克莱顿答案的回应,它声称它允许静态方法的inheritance。 它不。 静态方法不是多态的。 这是一个简短但完整的程序来certificate:
class Base { static void foo() { System.out.println("Base.foo()"); } } class Derived extends Base { static void foo() { System.out.println("Derived.foo()"); } } public class Test { public static void main(String[] args) { Base b = new Derived(); b.foo(); // Prints "Base.foo()" b = null; b.foo(); // Still prints "Base.foo()" } }
如您所见, b
的执行时间值完全被忽略。
因为语言规范允许:)就像VB一样,C#没有。
为什么它应该是一个错误? 该实例可以访问所有的静态方法。 静态方法不能改变实例的状态(试图是一个编译错误)。
你给出的众所周知的例子的问题是非常具体的线程 ,而不是静态的方法调用。 看起来好像你正在为线程引用的线程获取activeCount()
,但是你真的获得了调用线程的计数。 这是一个程序员正在做的逻辑错误。 发出警告是编译器在这种情况下做的适当的事情。 您需要注意警告并修复您的代码。
编辑:我意识到,语言的语法是什么让你写错误的代码,但请记住,编译器及其警告也是语言的一部分。 该语言允许你做一些编译器认为可疑的事情,但它会给你一个警告,以确保你知道它可能会导致问题。
由于所有已经存在的代码,它们不能再成为一个错误了。
我和你在一起,这应该是一个错误。 也许应该有编译器的选项/configuration文件升级一些警告错误。
更新:当他们在1.4中引入了与旧代码相似的潜在兼容性问题的assert关键字时, 只有当你明确地将源代码模式设置为“1.4”时 ,它才能使用它。 我想在新的源代码模式“java 7”中可能会出错。 但我怀疑他们会这样做,考虑到它会造成的一切麻烦。 正如其他人所指出的,防止你编写混淆的代码并不是必须的。 Java的语言变化应该被限制在这一点上是绝对必要的。
简短的回答 – 语言允许它,所以它不是一个错误。
这不是一个错误,因为它是规范的一部分,但是你显然在问起这个我们都可以猜测的基本原理。
我的猜测是,这个来源实际上是允许类中的方法在同一个类中调用一个静态方法而没有麻烦。 由于调用x()是合法的(即使没有自己的类名),调用this.x()也应该是合法的,因此通过任何对象调用也是合法的。
这也有助于鼓励用户在不改变状态的情况下将私有函数变为静态。
另外,编译器一般都会尽量避免在没有办法导致直接错误的情况下声明错误。 由于静态方法不会改变状态或关心调用对象,因此不会导致实际的错误(只是混淆)。 警告就足够了。
从编译器的angular度来看,真正重要的是能够parsing符号。 在静态方法的情况下,它需要知道要查找哪个类 – 因为它不与任何特定的对象关联。 Java的devise者明显地决定,既然他们可以确定一个对象的类,他们也可以从该对象的任何实例中parsing该对象的任何静态方法的类。 他们select允许这个 – 也许是由TofuBeer的观察所左右 – 给程序员一些方便。 其他语言devise师做出了不同的select。 我可能会陷入后一阵营,但这对我来说并不是什么大事。 我可能会允许@TofuBeer提到的用法,但允许我的立场,不允许从一个实例variables的访问不成立。
也许你可以在你的IDE中改变它(在Eclipse Preferences – > Java – > Compiler – > Errors / Warnings中)
可能是相同的逻辑,这使得这不是一个错误:
public class X { public static void foo() { } public void bar() { foo(); // no need to do X.foo(); } }
实例variables引用的目的只是提供包含静态的types。 如果您查看通过instance.staticMethod调用静态的字节代码,或者EnclosingClass.staticMethod产生相同的调用静态方法字节码。 不会引用该实例。
答案也是为什么它在那里,好吧。 只要你使用这个类。 而不是通过一个实例,你将有助于避免将来的混淆。
没有select它。 在Java中(与许多其他语言一样),您可以通过类的类名或实例对象访问类的所有静态成员。 这将取决于你和你的案例和软件解决scheme,你应该使用,这给你更多的可读性。
我只是想这个:
instanceVar.staticMethod();
为这个简写:
instanceVar.getClass().staticMethod();
如果你总是这样做:
SomeClass.staticMethod();
那么你将无法利用静态方法的inheritance。
也就是说,通过实例调用静态方法,您不需要知道实例在编译时具体是什么类,只需要在inheritance链的某处实现staticMethod()。
编辑:这个答案是错误的。 详情请参阅评论。