views:

785

answers:

13

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.

+8  A: 

Because the language spec allows it :) Just like VB does and C# doesn't.

Mehrdad Afshari
The spec allows it - but is there any case where it makes sense to do it?
tmtest
It's just another way to call it. Like you can access member fields with or without `this` keyword. Or calling static methods in the same class without specifying any class name. I like the C# way much better and wouldn't ever write such a code, but the language designers designed it like so.
Mehrdad Afshari
+2  A: 

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

Visage
+4  A: 

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.

Thilo
There is such option in Eclipse compiler (indirectStatic, http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.jdt.doc.isv/guide/jdt_api_compile.htm)
Peter Štibraný
But they did this when they introduced the assert keyword in Java 1.4. Why can't they do it again in the same way?
Hosam Aly
You're right. For Assert, you have to specifically set the compiler level to 1.4, otherwise it still compiles without assert. I suppose 1.5 annotations and generics work the same. So one could have a Java 7 compile level, that would make it an error.
Thilo
+1  A: 

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

Dutow
A: 

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.

Pooria
+1  A: 

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

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.

tvanfosson
+5  A: 

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.

Bill the Lizard
It's irrelevant to the *instance*. The only thing is the *declared type* of the variable. It's a horrible, horrible misfeature and a mistake on the part of the language designers, IMO.
Jon Skeet
Yes, it's irrelevant to the instance, but I don't think calling a static method from an instance should be a full-fledged error. It's just really bad style. IMHO, a class should either be all static methods, or the static methods should be private, but I wouldn't want the compiler to enforce that.
Bill the Lizard
@Bill: I don't think it even gave a warning in the early days. The warning was added because it was misleading people. What's the *benefit* in allowing it?
Jon Skeet
@Bill: Put it this way - if Java *didn't* allow it and someone suggested that it *should* allow it, what would your reaction be? Would you really argue in favour of it? Or would you argue (as I do) that it's a pointless feature which needlessly allows misleading code?
Jon Skeet
@Jon: The benefit in allowing it could probably only be expressed in compile time. There's no reason from our perspective to need to call a static method from an instance. Since no other language feature would disallow it, the earliest implementors probably left it out as seemingly needless check.
Bill the Lizard
@Jon: I do agree, though, that if it were an error from the beginning, I would not argue in favor of allowing it. You can already call <Class>.staticMethod(), so there's really no need for <instance>.staticMethod().
Bill the Lizard
I don't buy the "compile time" reason - it's obvious that you're looking up the method via a reference; how long does it then take to check whether or not it's static? I think it's easier just to consider it a language design mistake.
Jon Skeet
But not having the hindsight of seeing all the bad code we would write without a warning for that, why would you put an extra check in when you're writing the compiler? The reference has access to static methods, so the warning requires extra code to check for it.
Bill the Lizard
When you're writing the compiler, you're bound by the language. However, I'd say that when designing the language you should recognise that it's a mistake to allow it. Anything reached via an expression should depend on the value of that expression in some way.
Jon Skeet
I don't know how obvious it is without the benefit of hindsight. There's good reason to allow access to static member variables and methods from an instance of a class. It doesn't seem like much of a stretch to allow it from references as well. Most of the time it's harmless for reasons <continued>
Bill the Lizard
...I've already explained. It's not until you're shown the bad code examples that it gives rise to that it's obviously not a good thing.
Bill the Lizard
That's why most of us shouldn't be language designers :)
Jon Skeet
No, as far as I know no one, not even McCarthy, gets it right the first time. Besides, I'm much more adept at complaining about existing languages. :)
Bill the Lizard
+8  A: 

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.

Jon Skeet
only if I had designed Java! lol!
Mehrdad Afshari
I think you may be missing something. See my post.
DJClayworth
@DJClayworth: Nope. That's allowed in C#, but someInstanceOfMyClass.doComplexCalculation() wouldn't be.
Jon Skeet
@Jon: I ran a similar test and confirmed your findings. Based on that I agree that it's wrong for Java to allow static methods to be called from an instance var. No capability is gained (as I originally thought), but the potential for confusion or error is increased.
Clayton
A: 

Because of cases like this:

class myClass {

   static void sMethod() {
     ...
     doComplexCalculation();
     ...
  }

  void iMethod() {
    ...
    doComplexCalculation();
    ...
  }

  private static void doComplexCalculation() {
    ...
  }
}

Without the ability to call static from instances you need to write doComplexCalculation() twice.

DJClayworth
You obviously want to keep ability to call static methods from instances. OP questioned rationale behind <instance>.staticMethod() syntax, when we have <Class>.staticMethod() already.
Peter Štibraný
+1 for Peter's response. There's nothing wrong with being able to call static methods without specifying the class name if the method is available already - but you shouldn't be able to call it via a reference *as if it were an instance method*.
Jon Skeet
OK, missed the point there.
DJClayworth
A: 

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.

Uri
@Uri: It's easy enough to make it available within the current class without specifying the class name. C# managed it, and Java compilers manage to tell the difference (as you won't get a warning in that case) so why should it be specified this way?
Jon Skeet
A: 

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.

mP
A: 

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.

Clayton
Good, that's another reason why it shouldn't be allowed - because you're being misled too. It doesn't act as you want it to. It will *only* use the *declared* type of "instanceVar" in your example. Heck, the value of instanceVar can even be null.
Jon Skeet
@Jon: I was under the impression that getClass() would give you the actual class of the object, not the declared type of the variable. Javadoc is unclear on this issue, so I'm going to test it.
Clayton
@Clayton: Yes, but it doesn't call getClass(). I'll edit my answer with an example.
Jon Skeet
(And if it *did* call getClass(), it couldn't then call staticMethod(), because that isn't a method on Class<T>.)
Jon Skeet
@Jon: I stand corrected. instance.getClass().staticMethod() wouldn't compile. instance.staticMethod() went to the declared class, not the actual class. It seems there's no way to call a static method of an instance var's superclass from the instance, at least not without resorting to Reflection.
Clayton
Does this mean I have to downvote my own post?
Clayton
You could potentially delete it, and I can edit my post to not refer to it.
Jon Skeet
@Jon: I'm going to leave it here. I learn as much from mistakes as anything else, so it might help someone else to see this. Thanks for your help.
Clayton
@Clayton: You may want to make this community wiki so we can downvote it without penalizing you.
Bill the Lizard
@Bill: good suggestion - done.
Clayton