Why is dynamic polymorphism so useful?

F

failure_to

hello

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!

2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

thank you
 
J

John W. Kennedy

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!
2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

These questions are, in the most literal meaning of the word, nonsense.
Try starting over with a better textbook, because the one you're
learning from isn't working. Honestly, I'm not trying to blow you off,
but your misunderstanding of the situation is so severe that I'd have to
write ten or twenty pages to create an answer for you -- and I'm not a
teacher.
--
John W. Kennedy
"The bright critics assembled in this volume will doubtless show, in
their sophisticated and ingenious new ways, that, just as /Pooh/ is
suffused with humanism, our humanism itself, at this late date, has
become full of /Pooh./"
-- Frederick Crews. "Postmodern Pooh", Preface
 
L

Lew

hello

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!

Actually, not, because at compile time the actual type of the object isn't known.

Look at it this way, there are two things in a program that have types: the
variable and the object. Variables have compile-time type, objects have
run-time type. Variables have scope, objects have lifetimes. A variable can
go out of scope and disappear, but the object to which it pointed will live on.

Classic example:

List <String> messages = new ArrayList <String> ();

The variable 'messages' has only the compile-time type List <String>. Thus it
only can invoke methods known to the interface.

The actual object has a run-time type of ArrayList. This can change during
the program's lifetime:

later:
messages = new TreeList <String> ();

Now the run-time of the object has changed, perhaps in response to some
condition that doesn't always happen.

The compile-time type remains List. No way the compiler can know ahead of
time that at certain times the dispatch for add() will have to follow a
different logic path. That happens through the object, not the variable, at
run time, not compile time.
2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

I don't remember them off the top of my head, perhaps it was from the Java
Language Specification (JLS) itself, but there are explanations you can
quickly google up.

My take on it is that you need an object to dispatch through, because it
depends on run-time type. There is no object, nor run-time type to a class -
a class is a class. All right, there is a kind of "object", but it's not at
all the same kind as the Java language means by an "object".

The class thing carries a bunch of information that pertain to that class.
There's nothing to dispatch through - no change in a class's state that says,
"OK, now we dispatch a method" - because there is no object of a subtype to
say, "Use my stuff." There's just the class, itself.

So you call a static method Foo.doGlobalThing() - based on what would you say,
"dispatch to a subtype"? Nothing, that's what. You have to call the
doGlobalThing() in the Foo class - you can't know what subtype might be
intended. The notion of an override has no referent, therefore no meaning.
 
R

Roedy Green

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!

You can get a more accurate match. If a reference is an Object but
the object itself is a Rabbit, you can get the specialised Rabbit
method. Java does not know to use the specialised Rabbit method at
compile time.

There is extra overhead for runtime matching. Even without it, you
can in your general method look for special cases and dispatch them to
more special methods. To me, the problem does not come up often
enough for it to be worth the overhead.

Over time that tradeoff my change. Lots of thing we would not dream
of doing before for programmer convenience become necessities with
sufficient RAM and CPU power.
 
R

Roedy Green

2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

You would need a reference that could refer to several different
class methods. That sounds a lot like a standard object pointer. So
we are right back to ordinary instance methods.
 
E

Eric Sosman

hello

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!

Please explain how the compiler can figure out which class'
method to call in

Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString(); // Integer's toString, or Double's?
2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

When calling a static method, what is the `this' object whose
actual nature would determine the method to be called?
 
W

Wayne

Eric said:
Please explain how the compiler can figure out which class'
method to call in

Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString(); // Integer's toString, or Double's?

The best way to learn is to write programs and examine the results.
Here I created foo.java:

class foo {
public static void main (String[]args) {
Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString();
}
}

Compiled with "javac foo.java", then running "javap -s -c foo"
produces:

Compiled from "foo.java"
class foo extends java.lang.Object{
foo();
Signature: ()V
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
Code:
0: invokestatic #2; //Method java/lang/Math.random:()D
3: ldc2_w #3; //double 0.5d
6: dcmpg
7: ifge 26
10: new #5; //class java/lang/Integer
13: dup
14: bipush 42
16: invokespecial #6; //Method java/lang/Integer."<init>":(I)V
19: invokevirtual #7; //Method java/lang/Integer.intValue:()I
22: i2d
23: goto 39
26: new #8; //class java/lang/Double
29: dup
30: ldc2_w #9; //double 42.0d
33: invokespecial #11; //Method java/lang/Double."<init>":(D)V
36: invokevirtual #12; //Method java/lang/Double.doubleValue:()D
39: invokestatic #13; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
42: astore_1
43: aload_1
44: invokevirtual #14; //Method java/lang/Object.toString:()Ljava/lang/String;
47: astore_2
48: return

}

===================================================

Even if you don't understand all of the JVM instructions you should be
able to follow this code: call Math.random, compare the result to 0.5,
if less then create a new Integer (lines 10-23), else create a new
Double (lines 26-38). The value is converted to a String in lines 39-47.
It should be clear from line 39 the type of the expression you questioned
is...irrelevant! Note the "i2d" instruction on line 22. The compiler
is pulling the primitive int from the Integer (line 19), converting to a
double (line 22). On the other hand, if a Double is created, its value
is extracted to a primitive (line 36).

Then in either case a third object, a Double, is created from that
double (line 39). However the compiler invokes Object.toString, not
Double.toString (line 44) for reasons that elude me; perhaps an
optimizing compiler would use the more specific method.

When in doubt you can check the JVM specs (2nd ed is available
on-line). Playing around like this is a terrific way to learn.
When calling a static method, what is the `this' object whose
actual nature would determine the method to be called?

The keyword "this" is normally bound to the Object reference the
method is invoked on. But for static methods, "this" keyword isn't
set at all and can't be used.

A mental model can simplify your thinking here (even if it is incorrect).
Every class is represented by an Object, created in a special way by the
ClassLoader, of type java.lang.Class. You can think of any methods
and fields declared as static as methods and fields of that class Object
(again, that's not really correct). Thus, when you create a class Foo
and some Foo objects you also have a Class object named "Foo", where
the system keeps the Foo class static fields (a.k.a. "class variables")
and methods, along with the ability to create objects of type Foo.
So when you invoke a static method the "this" reference, if it existed,
would not refer to any Foo type object but rather to the Class object
named Foo. Thus such a method has access to static fields and other
static methods, but this Class object named Foo doesn't contain any
instance methods or fields. (This mental model is too simple to
explain how it really works but it should help with understanding
this one aspect of static versus instance. Please don't shoot me.)

Consider the method main: when starting the JVM, you specify a
class, not a class and method. The JVM will create the Class
object for the specified class but no other objects. So only
a static method could be invoked at that time, which is why
the main method must be static.

While some languages only have (the equivalent of) instance fields
and methods, Java provides you the choice. The annoying part
is that Java uses the same operator to access static and instance
fields and methods leading to some confusion. No doubt keeping
the number of operators low(er) made the compilers easier to write.

So if you want per-object properties use instance variables ("fields").
If you want single "global" or "shared" properties use static
variables (or fields). If you want polymorphism use instance methods.
If you don't and you don't need access to instance fields, use
static methods.
From your post I'm guessing you question the wisdom of providing
non-polymorphic methods. You are not alone but personally I often
find them useful, especially for collections of "utility" methods
that don't need access to any instance fields. (Take a look at the
utility classes in java.lang and java.util packages. You'll find
most of the methods of these classes are declared static.)

Hope this helps!

-Wayne
 
L

Lasse Reichstein Nielsen

Wayne said:
The best way to learn is to write programs and examine the results.
Here I created foo.java:

class foo {
public static void main (String[]args) {
Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString();
}
}

Compiled with "javac foo.java", then running "javap -s -c foo"
produces: ....
Note the "i2d" instruction on line 22. The compiler
is pulling the primitive int from the Integer (line 19), converting to a
double (line 22). On the other hand, if a Double is created, its value
is extracted to a primitive (line 36).

I can see that the conditional expression caused some unexpected coercions.
Try this instead:

Number n;
if (Math.random() < 0.5) {
n = new Integer(42):
} else {
n = new Double(42.0);
}

or perhaps just

Number n = (Math.random() < 0.5) ? (Number) new Integer(42)
: (Number) new Doubler(42.0);

Then in either case a third object, a Double, is created from that
double (line 39). However the compiler invokes Object.toString, not
Double.toString (line 44) for reasons that elude me; perhaps an
optimizing compiler would use the more specific method.

It calls Object.toString virtually, because that is the most specific
toString that exists on the Number class.

It would be possible to optimize, if an analysis showed that only
Double values would ever become that value of Number type
variable. That analysis would have to be absolutely certain, even in
the presence of multiple threads simultaneously fiddling with other
object's fields using reflection. A mistake in the analysis would make
it into the class file, and the JVM would have no chance of correcting
it. That is why most optimizations are better left for the runtime
HotSpot optimizer, since it can optimize based on what is actually
happening, and can backtrack and use the unoptimized version if
the optimziation enabling conditions change.

In some cases (e.g., the example using "if" above), you can't know at
compile time what type of object it will be, so no compiler optimization
is possible.

/L
 
M

Mark Rafn

Homework? Hate to encourage posting your class questions, but these come up
occasionally to non-school seekers of language design.

1) With dynamic polymorphism, calls to actual methods are resolved at
run time. So why is that so important? Why is that so much better
than letting the compiler figure out which version of method to call
( based on the type of object being referred by reference variable )?

1a) Because the compiler cannot know what the runtime environment will be, and
what class implementations will be loaded.

1b) The compiler DOES decide this. Not the java->bytecode compiler, but the
bytecode->machine just-in-time compiler that implements the runtime. And it
re-decides as things change.
Result would still be the same ... if superclass variable referred to
child class object, then method of child class object would be
called!

It can change between compiliation and runtime. Heck, it can change during
runtime.
2) Why isn't polymorphism also enabled for resolving calls to static
methods? Wouldn't there be some benefits by allowing that?

How would it work? Static methods have no object reference, so there are
never multiple different possibilities.
 
L

Lew

Lasse said:
Wayne said:
The best way to learn is to write programs and examine the results.
Here I created foo.java:

class foo {
public static void main (String[]args) {
Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString();
}
}

Compiled with "javac foo.java", then running "javap -s -c foo"
produces: ....
Note the "i2d" instruction on line 22. The compiler
is pulling the primitive int from the Integer (line 19), converting to a
double (line 22). On the other hand, if a Double is created, its value
is extracted to a primitive (line 36).

I can see that the conditional expression caused some unexpected coercions.
Try this instead:

Number n;
if (Math.random() < 0.5) {
n = new Integer(42):
} else {
n = new Double(42.0);
}

or perhaps just

Number n = (Math.random() < 0.5) ? (Number) new Integer(42)
: (Number) new Doubler(42.0);

The important point being that the compiler can have no way of knowing which
result will pertain at run time. No need to read assembler for that; it's
plenty clear from just the Java code.
 
W

Wayne

Lew said:
Lasse said:
Wayne said:
The best way to learn is to write programs and examine the results.
Here I created foo.java:

class foo {
public static void main (String[]args) {
Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString();
}
}

Compiled with "javac foo.java", then running "javap -s -c foo"
produces: ....
Note the "i2d" instruction on line 22. The compiler
is pulling the primitive int from the Integer (line 19), converting to a
double (line 22). On the other hand, if a Double is created, its value
is extracted to a primitive (line 36).

I can see that the conditional expression caused some unexpected
coercions.
Try this instead:

Number n;
if (Math.random() < 0.5) {
n = new Integer(42): } else {
n = new Double(42.0);
}

or perhaps just

Number n = (Math.random() < 0.5) ? (Number) new Integer(42)
: (Number) new Doubler(42.0);

The important point being that the compiler can have no way of knowing
which result will pertain at run time. No need to read assembler for
that; it's plenty clear from just the Java code.

Hmm. Could someone post an SSCCE using standard Java classes,
where the polymorphism shows in the output of javap? All my
attempts at using toString result in Object.toString method
being invoked.

-Wayne
 
L

Lew

Wayne said:
Hmm. Could someone post an SSCCE using standard Java classes,
where the polymorphism shows in the output of javap? All my
attempts at using toString result in Object.toString method
being invoked.

Perhaps something like a Collection add() where the implementation is
ArrayList or LinkedList or TreeSet or HashSet, chosen at runtime?
 
W

Wayne

Wayne said:
Hmm. Could someone post an SSCCE using standard Java classes,
where the polymorphism shows in the output of javap? All my
attempts at using toString result in Object.toString method
being invoked.

Or perhaps even better, is there a freely available debugger
(say in Netbeans or Eclipse) that can display an object's
vtable (or whatever its called in Java)? And to save me
hours of learning what is that debugger's command(s) to
do that?

-Wayne
 
F

failure_to

hello


Homework? Hate to encourage posting your class questions, but these come up
occasionally to non-school seekers of language design.

Mark Rafn (e-mail address removed) <http://www.dagon.net/>

Chances are you prob won't read this post, but still...

I'd appreciate if you don't assume too much about the nature of my
questions since people on this forum may start to believe you and
decide not to help me and then I'm f*****. At least this way I can
get a fair chance of getting people to help me when I can't figure out
the stuff on my own. Try to see this from my perspective

BTW-I'm not in any school and thus I'm learning programming by myself
and for myself, and if I get stuck on some topic, this group is my
best chance ( contrary to your belief, I don't have a professor I can
run to for help )

I hope I haven't offend you with my little rant, since that wasn't my
intention


thank you all for your kind help

cheers
 
M

Mark Rafn

Chances are you prob won't read this post, but still...
I'd appreciate if you don't assume too much about the nature of my

I usually read direct followups to my posts. I didn't mean any offense,
and I did, in fact, both state that it might not be homework AND give my
answer. Mostly I wanted to let folks know that it might NOT be homework, to
avoid the common "we don't answer homework" one-liners...

Anyway, what did you think of the answers? Make sense? Raise other
questions?
 
L

Lew

At least this way I can
get a fair chance of getting people to help me when I can't figure out
the stuff on my own. Try to see this from my perspective

You will note that your post got many useful answers by several people who
made no overt assumptions about your purpose.
 
F

failure_to

hello


* I must first point out that I realize ( even when I first started
this thread ) that with the help of polymorphism we can get a generic
interface and thus don¡¦t have to change a code when new subclass is
introduced!




1) Now what is most bugging me about polymorphism is the following:

I also realize, that with polymorphism we can run a program and while
program is already running, we can introduce a totally new class ( let
us call this new class N_C ) and thanx to polymorphism this program
may operate on instances of N_C without the need to recompile or even
without the need to restart this program.


But how to write a code for this program in such flexible way that
program will KNOW that new class is present and operate on it is
beyond me. Let me explain what I mean:

Say we have superclass A and bunch of subclasses ( all of them
override A¡¦s method a1() ). Now somewhere in this program is a code
that calls a1() via reference variable ref_var:

* A ref_var; // here we define ref_var

* At some point in a program we assign to ref_var a reference to some
object ( this object might be of class type A or one of its
subclasses )

*then we call ref_var.a1(); // which method to actually call is
resolved at runtime


Say we start this app and while app is already running, we write a new
class called N_C.
The already running app has to be flexible enough to enable an object
of type N_C not only to be added to running app ( without even
restarting this app ), but to actually use this object.
But how do we do that? Let me explain it further:


Say this running app has an object of type B, where B is subclass of
A, and if I want ref_var to reference this object, then I do this by
writing in program code the following line:

B b = new B():
ref_var = b;

The above lines are hard coded into program. But how do we assign
ref_var a reference to object of type N_C, if we introduce this new
class to already running app --> somewhere in the program code the
following two lines would have to be written:

N_C n_c = new N_C();
ref_ver = n_c;

But problem is app is already running and we don¡¦t want to stop this
app to write the above two new lines and yet we want this app to
somehow use this new object ( by assigning ref_var a reference to
n_c ). Now how can assign ref_var a reference to object of type N_C
without adding the above two lines into code ?





Anyway, what did you think of the answers? Make sense? Raise other
questions?

Perhaps I shouldn't say this, cos you went through so much trouble in
order to help me, but truth be told questions were a bit too technical
for me and even if I did somewhat understand what ( some of ) you were
saying, I didn¡¦t understand why would arguments you provided prove
anything ( at least I didn¡¦t find them to prove anything, but then
again, I'm not exactly ...). But I do know that in time ( when I get a
bit better in programming ) these answers will be of great value



I will give just a few examples:

You can get a more accurate match. If a reference is an Object
but the object itself is a Rabbit, you can get the specialised
Rabbit method. Java does not know to use the specialised Rabbit
method at compile time.

Why couldn¡¦t java know at compile time what to do with rabbit method?
Perhaps because creators of Java decided compiler doesn¡¦t have to know
that, since, afteral, they were gonna implement dynamic binding, or is
it in general not possible for compiler to know that, even if creators
of Java wanted for compiler to know that?



Please explain how the compiler can figure out which class'
method to call in
Number n = (Math.random() < 0.5)
? new Integer(42) : new Double(42.0);
String s = n.toString(); // Integer's toString, or Double's?

I don¡¦t understand how this proves anything? Perhaps if this was done
at compile time, then internally ( I¡¦m talking out of my arse now )
there could be another

if ( n is integer then call n.toString(Integer n) )
else ( else if n is double then call n.toString( Double n ) )

statement that enable the compiler to "call" the appropriate method.



It can change between compiliation and runtime. Heck, it can
change during runtime.

What can change?

BTW - I do realize that compiler can¡¦t resolve method calls due to
Java ability of introducing new classes to program ( without the need
to recompile a program )„³ thus compiler has no way of knowing what
classes may be added to program in the future ( the questions I
originally asked ignored on purpose this Java ability )!


uh
 
L

Lew

I also realize, that with polymorphism we can run a program and while
program is already running, we can introduce a totally new class ( let
us call this new class N_C ) and thanx to polymorphism this program
may operate on instances of N_C without the need to recompile or even
without the need to restart this program.


But how to write a code for this program in such flexible way that
program will KNOW that new class is present and operate on it is
beyond me.

Java has basically three ways to accomplish this: classloaders, reflection and
the debugger API.

All three are advanced topics. Let me give just the briefest summary.

Classloaders can be directed to reach out to a known source and load the
bytecode for a class while the program is running.

Reflection lets a program retrieve instances from classes not known until run
time, perhaps ones that have been retrieved through a custom classloader.

The debugger API has hooks that let a program substitute different class
versions for each other while the program is running.

Didn't catch who said:
Why couldn’t java know at compile time what to do with rabbit method?

That's "Java", not "java".

This was explained a few times in this thread. It's not what "Java" knows,
it's what the variable type is, that determines what methods the variable can
call. Remember from upthread that variables have compile-time type, objects
have run-time type.
Perhaps because creators of Java decided compiler doesn’t have to know
that, since, afteral, they were gonna implement dynamic binding, or is
it in general not possible for compiler to know that, even if creators
of Java wanted for compiler to know that?

No need to get religious - the creators of Java have nothing to do with this.

Java is defined by a set of rules, and those rules determine the behavior.
The key is to remember that variables have only compile-time type - objects
have run-time type. The variable knows how to ask for something in the
compile-time type, e.g., "x.run()" for a variable "x" of type "Runnable".
Since there is no information in the compiler about the run-time type of the
actual object, it is only valid to say that "x" calls its "run()" method. The
actual object at run time will pick up the request for a "run()" and simply
use its own method to do it. That's all.
I don’t understand how this proves anything? Perhaps if this was done
at compile time, then internally ( I’m talking out of my arse now )
there could be another

The whole point is that the compiler only knows that the variable 'n' is a
'Number'. No more is needed, actually.

At run time, the object will pick up the call to toString(), and use its own
version of toString() to fulfill the call, is all.
if ( n is integer then call n.toString(Integer n) )
else ( else if n is double then call n.toString( Double n ) )

No need for that when the object itself already knows how to toString()
itself. It's called separation of concerns, and encapsulation. The calling
method doesn't need to care about the details - it just trusts the object to
call its own method on its own.
statement that enable the compiler to "call" the appropriate method.

Compilers don't call methods, objects do. Compilers just tell the object
which method to call.

How would a compiler know what object is in play in the future?
What can change?

The object that is asked to perform the method can change.
BTW - I do realize that compiler can’t resolve method calls due to
Java ability of introducing new classes to program ( without the need
to recompile a program )迳 thus compiler has no way of knowing what
classes may be added to program in the future ( the questions I
originally asked ignored on purpose this Java ability )!

This has nothing to do with polymorphism. Polymorphism is simply that the
object decides for itself how to execute a method.
 
P

Patrick May

1) Now what is most bugging me about polymorphism is the following:

I also realize, that with polymorphism we can run a program and
while program is already running, we can introduce a totally new
class ( let us call this new class N_C ) and thanx to polymorphism
this program may operate on instances of N_C without the need to
recompile or even without the need to restart this program.

But how to write a code for this program in such flexible way that
program will KNOW that new class is present and operate on it is
beyond me. Let me explain what I mean:

Here's a simple example:

http://www.spe.com/Chain_of_Responsibility.html

Does that help?

Regards,

Patrick
 
T

Tim Smith

The important point being that the compiler can have no way of knowing which
result will pertain at run time. No need to read assembler for that; it's
plenty clear from just the Java code.

It depends on how sneaky the compiler wants to be. If it notices that
this code is only called once, couldn't it generate the random number at
compile time, and then just compile the appropriate branch?
 

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,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top