为什么不是';通过实例调用静态方法对Java编译器来说是一个错误吗?

为什么不是';通过实例调用静态方法对Java编译器来说是一个错误吗?,java,static,methods,Java,Static,Methods,我相信你们都知道我所说的行为——比如: Thread thread = new Thread(); int activeCount = thread.activeCount(); 引发编译器警告。为什么这不是一个错误 编辑: 需要明确的是:这个问题与线程无关。我意识到在讨论这个问题时经常会给出线程示例,因为它们可能会把事情弄得一团糟。但真正的问题是,这种用法总是胡说八道,你不能(胜任地)写这样一个电话并认真对待它。这种方法调用的任何例子都是barmy。还有一个: String hello =

我相信你们都知道我所说的行为——比如:

Thread thread = new Thread();
int activeCount = thread.activeCount();
引发编译器警告。为什么这不是一个错误

编辑:

需要明确的是:这个问题与线程无关。我意识到在讨论这个问题时经常会给出线程示例,因为它们可能会把事情弄得一团糟。但真正的问题是,这种用法总是胡说八道,你不能(胜任地)写这样一个电话并认真对待它。这种方法调用的任何例子都是barmy。还有一个:

String hello = "hello";
String number123AsString = hello.valueOf(123);

这使得它看起来好像每个字符串实例都有一个“String valueOf(int i)”方法。

简短的回答-语言允许它,因此它不是一个错误。

他们不能再将它作为一个错误,因为所有的代码都已经存在

我同意你的看法,那应该是个错误。 也许编译器应该有一个选项/配置文件来将一些警告升级为错误


更新:当他们在1.4中引入assert关键字时,发现它与旧代码有类似的潜在兼容性问题,他们成功了。我想在新的源代码模式“Java7”中可能会出现错误。但我怀疑他们会这么做,因为这会引起很多麻烦。正如其他人所指出的,严格来说没有必要阻止您编写混乱的代码。在这一点上,对Java的语言更改应该严格限制在必要的范围内。

也许您可以在IDE中更改它(在Eclipse首选项->Java->编译器->错误/警告中)

没有选择。在java中(和许多其他语言一样),您可以通过类的类名或实例对象访问该类的所有静态成员。这将取决于您和您的案例和软件解决方案,您应该使用哪一种解决方案来提高可读性。

对于相同的逻辑,这可能不是一个错误:

public class X
{
    public static void foo()
    {
    }

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}

从编译器的角度来看,真正重要的是它能够解析符号。对于静态方法,它需要知道查找它的类——因为它与任何特定对象都没有关联。Java的设计者显然决定,因为他们可以确定对象的类,所以他们也可以从对象的任何实例中解析该对象的任何静态方法的类。他们选择允许这样做——也许是受@TofuBeer观察的影响——为程序员提供一些便利。其他语言设计师做出了不同的选择。我可能会落入后一阵营,但这对我来说没什么大不了的。我可能会允许@TofuBeer提到的用法,但允许了它之后,我关于不允许从实例变量访问的立场就不那么站得住脚了。

为什么会出错?实例可以访问所有静态方法。静态方法无法更改实例的状态(尝试更改是一个编译错误)

您给出的著名示例的问题非常特定于线程,而不是静态方法调用。看起来您正在为
thread
引用的线程获取
activeCount()
,但实际上您正在获取调用线程的计数。这是您作为程序员正在犯的一个逻辑错误。在这种情况下,编译器应该发出警告。这取决于您是否注意到警告并修复代码


编辑:我意识到语言的语法允许您编写误导性代码,但请记住,编译器及其警告也是语言的一部分。该语言允许您执行编译器认为可疑的操作,但它会向您发出警告,以确保您意识到它可能会导致问题。

基本上,我认为Java设计人员在设计该语言时犯了一个错误,由于涉及的兼容性问题,现在修复它为时已晚。是的,它会导致非常误导的代码。是的,你应该避免。是的,在我看来,您应该确保您的IDE配置为将其视为错误。如果您曾经自己设计过一种语言,请记住它是一个需要避免的示例:)

为了回应DJClayworth的观点,以下是C#中允许的内容:

为什么我认为这是误导?因为如果我看代码
someVariable.SomeMethod()
我希望它使用
someVariable
的值。如果
SomeMethod()
是静态方法,则该期望无效;密码在欺骗我。这怎么可能是件好事呢

奇怪的是,Java不允许您使用可能未初始化的变量来调用静态方法,尽管它将使用的唯一信息是变量的声明类型。这是一个前后矛盾、毫无帮助的混乱局面。为什么允许呢

编辑:这个编辑是对Clayton的回答的回应,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()"
    }
}

如您所见,
b
的执行时间值被完全忽略。

这不是一个错误,因为它是规范的一部分,但您显然在询问基本原理,我们都可以猜到

我的猜测是,这实际上是为了允许类中的一个方法调用同一个类中的一个静态方法而不费吹灰之力。由于调用x()是合法的(即使没有自己的类名),因此调用this.x()也应该是合法的,因此通过任何对象进行调用也是合法的

这也有助于鼓励用户在不更改状态的情况下将私有函数转换为静态函数

此外,编译器通常会在无法导致直接错误的情况下避免声明错误。因为静态方法不改变状态,也不关心调用obj的对象
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()"
    }
}
instanceVar.staticMethod();
instanceVar.getClass().staticMethod();
SomeClass.staticMethod();
$input.isEmpty()
$input.getClass()
$input.getClass().forName("java.io.FileDescriptor")
$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")