Why do static and non-static method names collide?

  • Thread starter =?ISO-8859-1?Q?Thomas_Gagn=E9?=
  • Start date
?

=?ISO-8859-1?Q?Thomas_Gagn=E9?=

If a "static" method is like a class method, doesn't it have a different scope
than an instance method by the same name? For a class TestClass, shouldn't:

TestClass.main(...);
and
TestClass tc = new TestClass();
tc.main(...);

be two different methods?

tgagne:/home/tgagne/work/java cat test.java
import java.util.*;

public class test {
public static void main(String argv[]) {
(new test()).main(argv);
}

public void main(String argv[]) {
int i;

for (i = 0; i < argv.length; i++)
System.out.println(argv);
}
}
tgagne:/home/tgagne/work/java javac test.java
test.java:8: main(java.lang.String[]) is already defined in test
public void main(String argv[]) {
^
1 error
 
M

Miguel De Anda

Thomas Gagné said:
If a "static" method is like a class method, doesn't it have a different scope
than an instance method by the same name? For a class TestClass, shouldn't:

TestClass.main(...);
and
TestClass tc = new TestClass();
tc.main(...);

be two different methods?

tgagne:/home/tgagne/work/java cat test.java
import java.util.*;

public class test {
public static void main(String argv[]) {
(new test()).main(argv);
}

public void main(String argv[]) {
int i;

for (i = 0; i < argv.length; i++)
System.out.println(argv);
}
}
tgagne:/home/tgagne/work/java javac test.java
test.java:8: main(java.lang.String[]) is already defined in test
public void main(String argv[]) {
^
1 error



If you have a class:

public class Test {
public static void main(String[] args) {
Test t = new Test();
t.doFoo();
}
public Test() { }
public static void foo() { System.out.println("foo"); }
public void doFoo() { this.foo(); }
}

The object t is able to access the static method as if were its own (using
this.foo()). The only thing is that it can't have any references to 'this'
inside foo. Why would you want to confuse yourself anyways? Its hard enough
to this mistake...

public void setText(String text) { text = text; }

.... in the middle of a bunch of other code.
 
P

pete kirkham

Thomas said:
If a "static" method is like a class method, doesn't it have a different
scope than an instance method by the same name? For a class TestClass,
shouldn't:

TestClass.main(...);
and
TestClass tc = new TestClass();
tc.main(...);

be two different methods?

There's no reason they /should/ be in the same namespace, but I guess
that the language designers happened to prefer being able to call a
static method like
this.main() or just main()
instead of
TestClass.main()
from inside a non-static method of TestClass. As to whether
((ThisClass)x).main() when x is null throws a NullPointerexception
depends on the implementation.

If the only way to call a static method was <classname>.<methodname>
then there's no ambiguity, and confusing TestClass.main and main isn't
that likely, but the language makes the convention that there's only one
namespace for method names in a class, allowing the invocation to be
abbreviated.

As it is also makes access modifiers on static methods a little more
reasonable: if ThisClass.main can only be accessed from object of
ThisClass, then it's kinda tied to the instance even if it can't modifiy
the instance.

Sometimes it would be nice if (foo.getClass().main()) also worked to
invoke a static method, ie that the static methods and fields were
indeed members of the class, but that would require a completely
different policy on return types and/or dispatch than Java has.


Pete
 
J

John C. Bollinger

Thomas said:
If a "static" method is like a class method, doesn't it have a different
scope than an instance method by the same name?

As a matter of fact, no it doesn't.
For a class TestClass,
shouldn't:

TestClass.main(...);
and
TestClass tc = new TestClass();
tc.main(...);

be two different methods?

There are some caveats involving the compile-time type of tc, but in the
above example the same method is invoked either way, and that method is
static if the code compiled at all. This exhibits a Java feature (many
would call it a misfeature) that static members of a class can be
accessed "through" class instances. Such access does not actually use
the instance, however -- it is (partially) bound at compile time based
on the compile-time type of the reference, then resolved to a specific
static member of the appropriate class at run time. It has high
confusion potential to access a static member through a reference,
actually, and most people who express an opinion about it around here
say "don't do it."


John Bollinger
(e-mail address removed)
 
J

Joona I Palaste

As a matter of fact, no it doesn't.
There are some caveats involving the compile-time type of tc, but in the
above example the same method is invoked either way, and that method is
static if the code compiled at all. This exhibits a Java feature (many
would call it a misfeature) that static members of a class can be
accessed "through" class instances. Such access does not actually use
the instance, however -- it is (partially) bound at compile time based
on the compile-time type of the reference, then resolved to a specific
static member of the appropriate class at run time. It has high
confusion potential to access a static member through a reference,
actually, and most people who express an opinion about it around here
say "don't do it."

In fact, in Java, even this works:

public class Test
{
private static void test()
{
System.out.println("Hello world!");
}

public static void main(String[] args)
{
((Test)null).test();
}
}

and produces the output "Hello world!". If you didn't know test() was
static, you would probably be scratching your head wondering how it
could ever work.

--
/-- Joona Palaste ([email protected]) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"The large yellow ships hung in the sky in exactly the same way that bricks
don't."
- Douglas Adams
 
T

Tor Iver Wilhelmsen

Thomas Gagné said:
If a "static" method is like a class method, doesn't it have a
different scope than an instance method by the same name?

No. ALL methods are in a (very loose!) way "class methods", but
instance methods get an auto-generated parameter for "this".

For static methods to be separate from instance methods, the keyword
"static" would need to be encoded into the method signature, and it
isn't.
 
P

pete kirkham

Tor said:
No. ALL methods are in a (very loose!) way "class methods", but
instance methods get an auto-generated parameter for "this".

For static methods to be separate from instance methods, the keyword
"static" would need to be encoded into the method signature, and it
isn't.

Since the static keyword is there in the source, and the stsaic bit is
set in the method info of the class file, and since the compiler/JVM
has to use this information to generate/use a different calling
mechanism, it is already part of the signature of the method in the
sense of the dispatch mechanism, and this difference could be used to
signify a seperate namespace.

for example

public class Foo1 {
public static void main (String args[]) {
new Foo2().bar();
}
}

public class Foo2 {
public void bar () {
System.out.println("bar");
}
}

javac Foo1
javac Foo2
java Foo1
-->
bar

public class Foo2 {
public static void bar () {
System.out.println("bar");
}
}

javac Foo2
java Foo1
-->
Exception in thread "main" java.lang.IncompatibleClassChangeError

ie the signature has been changed sufficently that the dispatch
mechanism can't call it.


Pete
 
T

Tor Iver Wilhelmsen

pete kirkham said:
Since the static keyword is there in the source,

Source is not checked at runtime
and the stsaic bit is set in the method info of the class file,
Correct

and since the compiler/JVM has to use this information to
generate/use a different calling mechanism,

That's compiler, yes. JVM uses a direct index.
it is already part of the signature of the method in the sense of
the dispatch mechanism,

But not in the sense of the method table or name relevant for
reflection, unless you want to change how those work.
 
P

pete kirkham

Tor said:
But not in the sense of the method table or name relevant for
reflection, unless you want to change how those work.

I'm not trying to change how it works, as the way it works saves
confusion between methods in the same class at the expense of confusion
as to what namespace the method new Foo().bar() is drawn from and the
occasional need to recompile despite the lack of source code changes in
a client; there are in effect two namespaces for methods in the JVM, and
there is a constraint that names in these spaces must not collide.

In addition to this constraint, there is a constraint that two methods
cannot have both identical argument types and identical names; strictly
the signature (as expressed in the name and type info constant
referenced by the method table) encodes the return type as well as the
argument types, so the dispatch mechanism could differentiate on this as
well, but it doesn't, and returns an error if there is a collision.

In Java there is no method table in the C++ sense; there is only a class
file format and a specification of the behaviour of the JVM.

One part of the description of a method in the class file and JVM spec
is its name, another its argument types and return type, another is a
bit flag for public/private/protected/package access and for
static/instance scope. Of these the argument types, the return type, and
the name and the static/instance bit must agree with the invoked method,
and the access flags must allow suitable access rights, for the runtime
to dispatch the method invocation.

There is no constraint that a JVM finds a method with the same name then
reports and error if the method differs in the status/instance regard,
or searches for all methods with the same static/instance bit and
returns an error than none can be found. In fact, the JVM spec (5.4.3.4)
says that "If C declares a method with the name and descriptor specified
by the method reference, method lookup succeeds" for _both_ static and
virtual methods; so strictly the example I posted _should_ work as the
spec does not differentiate between lookup from virtual and static
methods (despite having different instructions for them, and obviously
different stack states), but only differentiates between interface and
non-interface method invocations (which are completely the same as far
as the Java language is concerned). But the example I gave doesn't work
on any JVM or compiler I've used, and I wouldn't expect it to etiher.

All compilers/JVMs use the static keyword/bit to differentiate between
static and virtual methods. This is an inherent part of
that-which-is-used-to-identify-the-method. Normally,
that-which-is-used-to-identify-the-method is called its signature.

Because of the constraints on collisions within the static and instance
method namespaces, the parameters used to identify a method to the
reflection API (name + arguments type) suffice. They do not suffice for
non-reflective method invocation; this requires the static bit agrees
and the return type agrees; therefore the name given to the reflection
API is obviously a weaker specifier than that required by the rest of
the language's dispatch mechanism. The reflection API is a tool whose
form is defined by additional constraints placed on the language's
dispatch mechanism; if this mechanism was different, and had fewer
collision constraints, the inputs to the reflection API would have to
reflect this.

These were design decisions, and it is perfectly possible to devise
dispatch mechanisms where these constraints do not apply: for example
C++ loosens the return type constraint slightly in that you may return a
more specific type (though this difference is enough to cause linking
errors in Java, it wouldn't be enough to effect the reflection API
lookup arguments, again showing that the reflection API uses a weaker
specifier).

So apart from the reflection API, which was designed after the decision
to dispatch on name+argment types only was made, everywhere Java has to
identify a method it uses a richer encoding which includes whether the
method is static or instance, either by keyword in source code for the
compiler or a bit flag in compiled classes, along with the return type
as part of its signature.


Pete
 
T

Tor Iver Wilhelmsen

pete kirkham said:
All compilers/JVMs use the static keyword/bit to differentiate between
static and virtual methods. This is an inherent part of
that-which-is-used-to-identify-the-method. Normally,
that-which-is-used-to-identify-the-method is called its signature.

But not in Java, cf. the JVM spec:

2.10.2 Method Signature

The signature of a method consists of the name of the method and the
number and type of formal parameters (§2.10.1) of the method. A
class may not declare two methods with the same signature.

That's the part you want to change to be able to confuse the
programmer by allowing both a static and a non-static method with the
same name and parameters.

You may also want to change the JLS which allows a static member to be
accessed via an instance reference.

I cannot see you have shown any *benefits* from allowing same-named
static and non-static metods.
 
P

pete kirkham

Tor said:
But not in Java, cf. the JVM spec:

2.10.2 Method Signature

The signature of a method consists of the name of the method and the
number and type of formal parameters (§2.10.1) of the method. A
class may not declare two methods with the same signature.

That's the part you want to change to be able to confuse the
programmer by allowing both a static and a non-static method with the
same name and parameters.

Yes, I'd want to change the spec so that it talks about signature as
that used for dispatch, like most other dynamically linked systems do,
and so eliminate the inconsistancy:

The signature of a method consists of the name of the method, the
number and type of formal parameters (§2.10.1) of the method, the
return type and whether it is static or instance method. A change
to any part of the signature will cause the method not to be located
at link time.

As an aid to program clarity, classes may not declare two methods
whose signatures differ only in return type or instance/static
scope.

If I wanted to change the way the JVM operates, I would also add that a
virtual or interface method may be overridden by one which returns a
more specific type, eg
class Foo implements clonable {
public Foo clone () {...

which can be useful.
You may also want to change the JLS which allows a static member to be
accessed via an instance reference.

Yes. Static methods are never accessed by an instance reference, but by
the declared compile time type of an expression:

class A {
public static foo () {...

class B extends A {
public static foo () {...

A x = new B();
x.foo();

calls A.foo() as the type of the variable x is A, even though the type
of the value of x is B.

new B().foo()

calls B.foo() as the type of the stack slot (new B()) is pushed into is B.

It should not be inconsistant as to whether an overloaded method is
envoked by the type of the container of the expression or the type of
the value of the expression.

In a dynamically linked language it absolutely should not be necessary
to recompile a class from a source file which has not been changed in
order for it to link correctly.
I cannot see you have shown any *benefits* from allowing same-named
static and non-static metods.

No, but then I've been arguing that the language should match what the
machine does, and I haven't been arguing for allowing collision between
static and instance method names.


Pete
 
?

=?ISO-8859-1?Q?Thomas_Gagn=E9?=

Tor said:
I cannot see you have shown any *benefits* from allowing same-named
static and non-static metods.

It's not unusual in other OO languages to have class-side instance-creation
methods named identically to instance-side initialization methods--leaving it
to the programmer which they prefer to call. In most cases, the class-side
method calls the instance-side method after creating it.
 
C

cgbusch

There are some caveats involving the compile-time type of tc, but in the
above example the same method is invoked either way, and that method is
static if the code compiled at all. This exhibits a Java feature (many
would call it a misfeature) that static members of a class can be
accessed "through" class instances. Such access does not actually use
the instance, however -- it is (partially) bound at compile time based
on the compile-time type of the reference, then resolved to a specific
static member of the appropriate class at run time. It has high
confusion potential to access a static member through a reference,
actually, and most people who express an opinion about it around here
say "don't do it."

The trouble I have with accessing static methods through the class
name is it requires more code to be changed when refactoring occurs.
For example:
class X { static void foo(); }
class Y { static void foo(); }
....
X instance=new X();
X.foo(); //sprinkle in code 1 million times.

You now refactor your code to use Y. 1 million + 1 lines to change.
But if you used the instance to reference foo():
X instance=new X();
instance.foo(); //sprinkle in code 1 million times.
You only need to change 1 line:
Y instance=new Y();

This is why I hate casts. It sprinkles annoying non-refactorable
code, when the compiler could easily figure it out.
x=(StupidCast)object;
The compiler knows what type of object x is! It could put the cast in.
I would argue that the compiler should implicitly cast any object to
the left operand's type in an assignment. Imagine how much cleaner
Collections code would be.
(I argue that casts in an assignment does not produce less buggy code,
but rather increases it, since people are less willing to refactor.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top