Avoid calling non-final methods in a constructor: Applies to static methods too?

O

Oliver Wong

I'm aware of and follow the dogma that one should not call non-final
methods in a constructor. However, I'm wondering whether this applies to
static methods as well. My understanding of the reasoning behind this rule
is that subclasses may see fields in some unstable state, which tells me
that the rule need not apply to static methods. However, this code analysis
tool is reporting a warning with the following code:

<code>
public class Test {
public Test() {
staticMethod();
}

public static void staticMethod() {
//Does nothing.
}
}
</code>

Should I be declaring the staticMethod as being final? Will that even
have any effect on the semantics of the code? Or should I rather submit this
as a potential bug fix to the tools author?

- Oliver
 
C

Chris Uppal

Oliver said:
<code>
public class Test {
public Test() {
staticMethod();
}

public static void staticMethod() {
//Does nothing.
}
}
</code>

Should I be declaring the staticMethod as being final? Will that even
have any effect on the semantics of the code? Or should I rather submit
this as a potential bug fix to the tools author?

Final doesn't apply to static methods 'cos they don't override anyway (I don't
suppose Java will even let you combine the two, though I haven't tested it).
Unless the linting tool is just warning you about calling static methods /at
all/ (which seems a touch too sweeping even for my taste ;-) or -- more
likely -- warning you about calling a static method via an instance referrence
(in this case the implicit "this."), then I think it must be a bug.

-- chris
 
P

Piotr Kobzda

Oliver said:
Should I be declaring the staticMethod as being final? Will that even
have any effect on the semantics of the code?

The only benefit of using final static method is to prevent against
declaration of another static method with the same signature in a
subclasses, no other semantical consequences holds. In other words
static methods are always called in static way, that is using
invokestatic bytecode instruction (invokevirtual or invokespecial are
never used for static methods).
Or should I rather submit
this as a potential bug fix to the tools author?

I think you should.


Regards,
piotr
 
M

Mike Schilling

Final doesn't apply to static methods 'cos they don't override anyway (I
don't
suppose Java will even let you combine the two, though I haven't tested
it).
Unless the linting tool is just warning you about calling static methods
/at
all/ (which seems a touch too sweeping even for my taste ;-) or -- more
likely -- warning you about calling a static method via an instance
referrence
(in this case the implicit "this."),

Are you saying that in

void meth()
{
staticMethod();
}

static void meth2()
{
staticMethod();
}

static void staticMethod()
{
}

the first call to staticMethod() is done implicitly via "this" and while the
second call simply finds that staticMethod() is in scope? I am dubious :)
 
C

Chris Uppal

Mike said:
Are you saying that in

void meth()
{
staticMethod();
}

static void meth2()
{
staticMethod();
}

static void staticMethod()
{
}

the first call to staticMethod() is done implicitly via "this" and while
the second call simply finds that staticMethod() is in scope? I am
dubious :)

I'm by no means certain which of the two candidate "explanations" apply from
the JLS. I suspect that both apply. But it doesn't really matter -- the
question is whether Oliver's linting tool interpreted it like that.

BTW, I did try putting
static final void aMethod() {}
into a class and javac accepted it quite happily -- which strikes me as stupid.
So then I tried Piotr's suggestion -- that it prevents you from creating
another static method with the same name/signature in a subclass -- and he's
right. I think that's /really/ stupid...

-- chris
 
P

Piotr Kobzda

Chris said:
So then I tried Piotr's suggestion -- that it prevents you from creating
another static method with the same name/signature in a subclass -- and he's
right. I think that's /really/ stupid...

Not as stupid as it looks like, IMHO. ;)

Formally there are adequate paragraphs in JSL which explains why this
construct is allowed, in particular see 8.4.3.2, 8.4.3.3 and 8.4.8.2 (JLS3).

I've not based any solution on final static methods yet, but I can
imagine some potential usages of such kind of methods.

Having for example:

class A {
static final void m1() {}
}
class B extends A {
}

we can call A's class m1 method with:

A.m1();

or (what is a key here) with:

B.m1();


This is of course not any special property of final static methods
(non-final static methods can be invoked the same way). But thanks to a
final there is no possibility to hide our A's m1 method from any class
subclassing A (e.g. from B).

So using final static methods we can create a special methods (e.g.
factory, registration, authentication etc.). And we know that nowhere in
our class hierarchy is a method accidentally (or not) "replacing" our
method's functionality.

Of course, as I said before, all static methods calls are preformed in a
static way (that means methods lookup is performed in compile time
only). So even when we'll make A's m1 method non-final, and than add
method hiding it to class B, our both above example calls will still
call A's m1 until we do not recompile them again.


Regards,
piotr
 
C

Chris Smith

Piotr Kobzda said:
Not as stupid as it looks like, IMHO. ;)

Nah, it's definitely stupid. It assigns yet another completely new
meaning to final, which is only superficially similar to the existing
meaning for instance methods.
 
P

Piotr Kobzda

Chris said:
Nah, it's definitely stupid. It assigns yet another completely new
meaning to final, which is only superficially similar to the existing
meaning for instance methods.

Completely new meaning? That meaning is given to final at the beginning
of Java (check JLS first! ed.). Using final methods (8.4.3.3) prevents
us from _overriding_ (8.4.8.1) of instance methods or _hiding_ (8.4.8.2)
of static methods. Which in fact gives *very similar* meaning to final
in both cases, it simply prevents from providing a "new version" of
method in subclasses.
What's stupid in that?


Regards,
piotr
 
C

Chris Uppal

Piotr said:
Completely new meaning? That meaning is given to final at the beginning
of Java (check JLS first! ed.). Using final methods (8.4.3.3) prevents
us from _overriding_ (8.4.8.1) of instance methods or _hiding_ (8.4.8.2)
of static methods. Which in fact gives *very similar* meaning to final
in both cases, it simply prevents from providing a "new version" of
method in subclasses.
What's stupid in that?

Well, I'm still sticking by the word "stupid". In this particular case it's
conflating two notions which are a lot less closely related than they might
look to a beginner. That's confusing for beginners, and irritating for purists
(and are there any other categories of programmer I care about ? Hmm.... ;-)

More generally, it's part-and-parcel with the mess surrounding static method in
Java. It's not the static methods themselves that I object to (though I don't
think they are done right), but all sorts of silly little details in the spec,
and so on, give the (IMO very strong) impression that the Java designers simply
didn't understand the difference between static methods (more properly called
functions), and real (OO) methods. My three favourite examples.

1 (the famous one) You can invoke a static method "via" a reference to an
instance.

2 The recommended order of keywords gives access control primacy over the
static/non-static distinction, when it is /much/ less important.

3) The method resolution rules in the JVM /require/ that the JVM first finds
the method with the given name/signature, and only /after/ it has done that is
it allowed to check whether the discovered method is static or not (and to
throw an error if its of the wrong kind) -- even though invokestatic and
invokevirtual (and variants) are not interchangeable instructions.

I get the impression that the designers had some sort of fuzzy idea of "static"
members being somehow shared between all the instances. And that -- above
all -- strikes me as stupid.

-- chris
 
C

Chris Smith

Piotr Kobzda said:
Completely new meaning? That meaning is given to final at the beginning
of Java (check JLS first! ed.). Using final methods (8.4.3.3) prevents
us from _overriding_ (8.4.8.1) of instance methods or _hiding_ (8.4.8.2)
of static methods.

Indeed, and the part about "hiding" of static methods is new versus a
hypothetical language where it is illegal to use the final modifier on a
static method. That hypothetical language is more consistent and
predictable than Java, hence the adjective "stupid" attached to the
choice to allow final on static methods.
Which in fact gives *very similar* meaning to final
in both cases,

I don't think it's very similar at all. In one case, you are prevented
from redefining the behavior of existing code by changing the behavior
for an existing method when executed in the context of a subclass. In
the other place, you are prevented from using a certain identifier to
define a completely new and unrelated static method in a subclass. The
fact that these two entirely different things look similar is confusing
and counts as a fault in the language. Why would anyone want to define
a language feature as if to pretend that they really are similar, and
thus further the confusion?
 
C

Chris Smith

Chris Smith said:
I don't think it's very similar at all. In one case, you are prevented
from redefining the behavior of existing code by changing the behavior
for an existing method when executed in the context of a subclass.

Okay, so that was a little awkward. It can be cleaned up a little by
using more theoretical OO technology.

In one case, you are prevented from redefining the behavior of existing
code by changing the method by which an object of the subclass responds
to the same message. In the other place, you are prevented from
defining essentially a new message with the same name as the existing
message.
 
M

Mike Schilling

Chris Smith said:
Okay, so that was a little awkward. It can be cleaned up a little by
using more theoretical OO technology.

In one case, you are prevented from redefining the behavior of existing
code by changing the method by which an object of the subclass responds
to the same message. In the other place, you are prevented from
defining essentially a new message with the same name as the existing
message.

That's the point, I think. There is no relationship between the two methods
defined in

class Super{ public static void a() {}}
class Sub extends Super {public static void a() {} }

except that they have the same name. One hides the other, but that's quite
different from overriding it.
 
D

Dale King

Oliver said:
I'm aware of and follow the dogma that one should not call non-final
methods in a constructor. However, I'm wondering whether this applies to
static methods as well. My understanding of the reasoning behind this
rule is that subclasses may see fields in some unstable state, which
tells me that the rule need not apply to static methods. However, this
code analysis tool is reporting a warning with the following code:

That dogma is a little overstated. You do need to be careful when
calling non-final instance methods, but saying that you should never
call non-final methods goes a bit too far. The more precise rule is that
you should not call non-final methods whose implementations depend on
the instance state.

For example, I have no problem with code that calls methods that depend
only on constants. But the problem is there is no way to force that from
the superclass. It would be nice if there were an annotation to force
subclasses to not use instance state in a method.
 
P

Piotr Kobzda

Piotr said:
Having for example:

class A {
static final void m1() {}
}
class B extends A {
}

we can call A's class m1 method with:

A.m1();

or (what is a key here) with:

B.m1();

Heaving the above example the following description is not quite true...
Of course, as I said before, all static methods calls are preformed in a
static way (that means methods lookup is performed in compile time
only). So even when we'll make A's m1 method non-final, and than add
method hiding it to class B, our both above example calls will still
call A's m1 until we do not recompile them again.

We don't have to recompile it again to see a different results -- that's
my earlier intuitive feeling on how static final methods are treated by
the compiler.
I've checked resulting bytecode, and invokestatic B.m1() still exists
for the above last usage example.
Nevertheless B.m1() results in A.m1() invocation at runtime. That is
because of Java method resolution mechanism, which is common for both
static and instance methods (see JVMS 2nd ed. - 5.4.3.3 Method Resolution).

Thus we have a kind of dynamic lookup of static methods within class
hierarchy (starting class B in my example) -- similar to the lookup
performed during invokespecial instruction calls.


Backing to the discussion on meaning of final for static methods, I'm
still the one -- having respect to all opinions given here -- for whom
using it makes some sense, final simply gives us an ability to tell the
others: "don't care about hidings of this method" and that's all for me.


Regards,
piotr
 
C

Chris Uppal

Piotr said:
Thus we have a kind of dynamic lookup of static methods within class
hierarchy (starting class B in my example) -- similar to the lookup
performed during invokespecial instruction calls.

Yes. The JVM-level static method lookup is distinctly strange, and it has
definite resemblances to virtual method lookup. I have always considered this
to be a mix of (a) a sensible strategy to ensure binary compatibility as static
members are migrated around the class hierarchy, and -- less importantly -- (b)
another example of the arbitrary weirdness surrounding static members in Java.

Backing to the discussion on meaning of final for static methods, I'm
still the one -- having respect to all opinions given here -- for whom
using it makes some sense, final simply gives us an ability to tell the
others: "don't care about hidings of this method" and that's all for me.

Fair enough. I wasn't disputing your facts (indeed, I learned from you here);
and what's left is a value judgement and/or a question of interpretation. If,
as it seems to you, this feature is reasonably consistent or otherwise a good
design choice, then who can say you are wrong ? I, personally, disagree (and
it seems that Chris Smith does too -- we Chrises stick together), but
presumably a certain J Gosling would agree with your interpretation ;-)

-- chris
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top