为什么通过实例调用静态方法不是 Java 编译器的错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/610458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why isn't calling a static method by way of an instance an error for the Java compiler?
提问by tmtest
I'm sure you all know the behaviour I mean - code such as:
我相信你们都知道我的意思的行为 - 代码如:
Thread thread = new Thread();
int activeCount = thread.activeCount();
provokes a compiler warning. Why isn't it an error?
引发编译器警告。为什么不是错误?
EDIT:
编辑:
To be clear: question has nothing to do with Threads. I realise Thread examples are often given when discussing this because of the potential to really mess things up with them. But really the problem is that such usage is alwaysnonsense and you can't (competently) write such a call and mean it. Any example of this type of method call would be barmy. Here's another:
需要明确的是:问题与线程无关。我意识到在讨论这个问题时经常会给出 Thread 的例子,因为它们有可能把事情搞得一团糟。但真正的问题是,这样的用法总是无稽之谈,你不能(称职)写这样一个电话并认真对待。这种类型的方法调用的任何例子都会很糟糕。这是另一个:
String hello = "hello";
String number123AsString = hello.valueOf(123);
Which makes it look as if each String instance comes with a "String valueOf(int i)" method.
这使它看起来好像每个 String 实例都带有一个“String valueOf(int i)”方法。
采纳答案by Jon Skeet
Basically I believe the Java designers made a mistake when they designed the language, and it's too late to fix it due to the compatibility issues involved. Yes, it can lead to very misleading code. Yes, you should avoid it. Yes, you should make sure your IDE is configured to treat it as an error, IMO. Should you ever design a language yourself, bear it in mind as an example of the kind of thing to avoid :)
基本上我认为 Java 设计者在设计语言时犯了一个错误,并且由于所涉及的兼容性问题而修复它为时已晚。是的,它会导致非常误导的代码。是的,你应该避免它。是的,您应该确保您的 IDE 配置为将其视为错误,IMO。如果你自己设计一种语言,请记住它作为避免这种事情的一个例子:)
Just to respond to DJClayworth's point, here's what's allowed in C#:
只是为了回应 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()
{
}
}
Why do I think it's misleading? Because if I look at code someVariable.SomeMethod()
I expect it to use the value of someVariable
. If SomeMethod()
is a static method, that expectation is invalid; the code is tricking me. How can that possibly be a goodthing?
为什么我认为这是误导?因为如果我查看代码,someVariable.SomeMethod()
我希望它使用someVariable
. 如果SomeMethod()
是静态方法,则该期望无效;代码在欺骗我。怎么能说有可能是一个很好的事情吗?
Bizarrely enough, Java won't let you use a potentially uninitialized variable to call a static method, despite the fact that the only information it's going to use is the declared type of the variable. It's an inconsistent and unhelpful mess. Why allow it?
奇怪的是,Java 不允许您使用可能未初始化的变量来调用静态方法,尽管它要使用的唯一信息是变量的声明类型。这是一个不一致和无益的混乱。为什么允许?
EDIT: This edit is a response to Clayton's answer, which claims it allows inheritance for static methods. It doesn't. Static methods just aren't polymorphic. Here's a short but complete program to demonstrate that:
编辑:此编辑是对 Clayton 的回答的回应,该回答声称它允许静态方法的继承。它没有。静态方法不是多态的。这是一个简短但完整的程序来证明:
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()"
}
}
As you can see, the execution-time value of b
is completely ignored.
如您所见, 的执行时间值b
被完全忽略。
回答by PaulJWilliams
Short answer - the language allows it, so its not an error.
简短的回答 - 语言允许,所以它不是错误。
回答by Thilo
They cannot make it an error anymore, because of all the code that is already out there.
他们不能再犯错误了,因为所有的代码都已经存在了。
I am with you on that it should be an error. Maybe there should be an option/profile for the compiler to upgrade some warnings to errors.
我和你一样认为这应该是一个错误。也许应该有一个选项/配置文件供编译器将一些警告升级为错误。
Update:When they introduced the assertkeyword in 1.4, which has similar potential compatibility issues with old code, they made it available only if you explicitly set the source mode to "1.4". I suppose one could make a it an error in a new source mode "java 7". But I doubt they would do it, considering that all the hassle it would cause. As others have pointed out, it is not strictly necessary to prevent you from writing confusing code. And language changes to Java should be limited to the strictly necessary at this point.
更新:当他们在 1.4 中引入assert关键字时,该关键字与旧代码具有类似的潜在兼容性问题,只有在您明确将源模式设置为 "1.4" 时才使其可用。我想在新的源代码模式“java 7”中可能会出错。但我怀疑他们会这样做,考虑到它会造成的所有麻烦。正如其他人所指出的那样,并非绝对有必要阻止您编写令人困惑的代码。对 Java 的语言更改应仅限于此时绝对必要的更改。
回答by Dutow
Probably you can change it in your IDE (in Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)
可能你可以在你的 IDE 中改变它(在 Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)
回答by Pooria
There's not option for it. In java (like many other lang.) you can have access to all static members of a class through its class name or instance object of that class. That would be up to you and your case and software solution which one you should use that gives you more readability.
没有它的选择。在java(像许多其他语言一样)中,您可以通过类名或该类的实例对象访问类的所有静态成员。这取决于您和您的案例和软件解决方案,您应该使用哪种解决方案来提高可读性。
回答by TofuBeer
Likely for the same logical that makes this not an error:
可能因为相同的逻辑使这不是错误:
public class X
{
public static void foo()
{
}
public void bar()
{
foo(); // no need to do X.foo();
}
}
回答by tvanfosson
The really important thing, from the compiler's perspective, is that it be able to resolve symbols. In the case of a static method, it needs to know what class to look in for it -- since it's not associated with any particular object. Java's designers obviously decided that since they could determine the class of an object, they could also resolve the class of any static method for that object from any instance of the object. They choose to allow this -- swayed, perhaps, by @TofuBeer's observation -- to give the programmer some convenience. Other language designers have made different choices. I probably would have fallen into the latter camp, but it's not that big of a deal to me. I probably would allow the usage that @TofuBeer mentions, but having allowed it my position on not allowing access from an instance variable is less tenable.
从编译器的角度来看,真正重要的是它能够解析符号。在静态方法的情况下,它需要知道要查找的类——因为它不与任何特定对象相关联。Java 的设计者显然决定,既然他们可以确定对象的类,他们也可以从对象的任何实例解析该对象的任何静态方法的类。他们选择允许这一点——也许是受到@TofuBeer 的观察影响——给程序员一些便利。其他语言设计者做出了不同的选择。我可能会落入后一个阵营,但这对我来说没什么大不了的。我可能会允许@TofuBeer 提到的用法,
回答by Bill the Lizard
Why should it be an error? The instance has access to all the static methods. The static methods can't change the state of the instance (trying to isa compile error).
为什么它应该是一个错误?该实例可以访问所有静态方法。静态方法不能改变实例的状态(试图是编译错误)。
The problem with the well-known example that you give is very specific to threads, not static method calls. It looks as though you're getting the activeCount()
for the thread referred to by thread
, but you're really getting the count for the calling thread. This is a logical error that you as a programmer are making. Issuing a warning is the appropriate thing for the compiler to do in this case. It's up to you to heed the warning and fix your code.
您给出的著名示例的问题非常特定于线程,而不是静态方法调用。看起来好像您正在获得由activeCount()
引用的线程的thread
,但实际上您正在获得调用线程的计数。这是您作为程序员所犯的逻辑错误。在这种情况下,发出警告是编译器应该做的适当的事情。由您来留意警告并修复您的代码。
EDIT: I realize that the syntax of the language is what's allowingyou to write misleading code, but remember that the compiler and its warnings are part of the language too. The language allows you to do something that the compiler considers dubious, but it gives you the warning to make sure you're aware that it could cause problems.
编辑:我意识到语言的语法允许您编写误导性代码,但请记住,编译器及其警告也是语言的一部分。该语言允许你做一些编译器认为可疑的事情,但它会给你警告,以确保你意识到它可能会导致问题。
回答by Uri
It isn't an error because it's part of the spec, but you're obviously asking about the rationale, which we can all guess at.
这不是错误,因为它是规范的一部分,但您显然是在询问基本原理,我们都可以猜到。
My guess is that the source of this is actually to allow a method in a class to invoke a static method in the same class without the hassle. Since calling x() is legal (even without the self class name), calling this.x() should be legal as well, and therefore calling via any object was made legal as well.
我的猜测是,这实际上是为了允许类中的方法调用同一个类中的静态方法而没有麻烦。由于调用 x() 是合法的(即使没有 self 类名),调用 this.x() 也应该是合法的,因此通过任何对象调用也是合法的。
This also helps encourage users to turn private functions into static if they don't change the state.
这也有助于鼓励用户在不更改状态的情况下将私有函数变为静态。
Besides, compilers generally try to avoid declaring errors when there is no way that this could lead to a direct error. Since a static method does not change the state or care about the invoking object, it does not cause an actual error (just confusion) to allow this. A warning suffices.
此外,编译器通常会在不可能导致直接错误的情况下尝试避免声明错误。由于静态方法不会更改状态或关心调用对象,因此允许这样做不会导致实际错误(只是混乱)。一个警告就足够了。
回答by mP.
The purpose of the instance variable reference is only to supply the type which encloses the static. If you look at the byte code invoking a static via instance.staticMethod or EnclosingClass.staticMethod produces the same invoke static method bytecode. No reference to the instance appears.
实例变量引用的目的只是提供包含静态的类型。如果您查看通过 instance.staticMethod 或 EnclosureClass.staticMethod 调用静态的字节码,则会生成相同的调用静态方法字节码。不会出现对实例的引用。
The answer as too why it's in there, well it just is. As long as you use the class. and not via an instance you will help avoid confusion in the future.
答案也是为什么它在那里,好吧,它就是这样。只要你使用类。而不是通过实例,您将有助于避免将来出现混淆。