Why isn't calling a static method by way of an instance an error for the Java compiler?

JavaStaticMethods

Java Problem Overview


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 always nonsense 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:

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.

Java Solutions


Solution 1 - Java

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 :)

Just to respond to DJClayworth's point, here's what's allowed in 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 good thing?

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?

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:

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.

Solution 2 - Java

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 is a 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.

EDIT: I realize that the syntax of the language is what's allowing you 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.

Solution 3 - Java

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 assert keyword 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.

Solution 4 - Java

Short answer - the language allows it, so its not an error.

Solution 5 - Java

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.

Solution 6 - Java

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();
    }
}

Solution 7 - Java

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.

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.

Solution 8 - Java

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.

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.

Solution 9 - Java

Probably you can change it in your IDE (in Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)

Solution 10 - Java

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.

Solution 11 - Java

It's pretty old topic but still up-to-date and surprisingly bringing higher impact nowadays. As Jon mentioned, it might be just a mistake Java's designers made at the very beginning. But I wouldn't imagine before it can have impact on security.

Many coders know Apache Velocity, flexible and powerful template engine. It's so powerful that it allows to feed template with a set of named objects - stricly considered as objects from programming language (Java originally). Those objects can be accessed from within template like in programming language so for example Java's String instance can be used with all its public fields, properties and methods

$input.isEmpty()

where input is a String, runs directly through JVM and returns true or false to Velocity parser's output). So far so good.

But in Java all objects inherit from Object so our end-users can also put this to the template

$input.getClass()

to get an instance of String Class.

And with this reference they can also call a static method forName(String) on this

$input.getClass().forName("java.io.FileDescriptor")

use any class name and use it to whatever web server's account can do (deface, steal DB content, inspect config files, ...)

This exploit is somehow (in specific context) described here: https://github.com/veracode-research/solr-injection#7-cve-2019-17558-rce-via-velocity-template-by-_s00py

It wouldn't be possible if calling static methods from reference to the instance of class was prohibited.

I'm not saying that a particular programming framework is better than the other one or so but I just want to put a comparison. There's a port of Apache Velocity for .NET. In C# it's not possible to call static methods just from instance's reference what makes exploit like this useless:

$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

Solution 12 - Java

I just consider this:

instanceVar.staticMethod();

to be shorthand for this:

instanceVar.getClass().staticMethod();

If you always had to do this:

SomeClass.staticMethod();

then you wouldn't be able to leverage inheritance for static methods.

That is, by calling the static method via the instance you don't need to know what concrete class the instance is at compile time, only that it implements staticMethod() somewhere along the inheritance chain.

EDIT: This answer is wrong. See comments for details.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontmtestView Question on Stackoverflow
Solution 1 - JavaJon SkeetView Answer on Stackoverflow
Solution 2 - JavaBill the LizardView Answer on Stackoverflow
Solution 3 - JavaThiloView Answer on Stackoverflow
Solution 4 - JavaPaulJWilliamsView Answer on Stackoverflow
Solution 5 - JavatvanfossonView Answer on Stackoverflow
Solution 6 - JavaTofuBeerView Answer on Stackoverflow
Solution 7 - JavaUriView Answer on Stackoverflow
Solution 8 - JavamP.View Answer on Stackoverflow
Solution 9 - JavaDutowView Answer on Stackoverflow
Solution 10 - JavaPooriaView Answer on Stackoverflow
Solution 11 - JavaTomasz PolachowskiView Answer on Stackoverflow
Solution 12 - JavaClaytonView Answer on Stackoverflow