Behavior of OO

G

Gordon Beaton

On the other hand, if somewhere else, the following two methods are
defined:
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

No, the most specific one is called in each case, so both your methods
are used in this example and the rest of your questions are moot.

For a long discussion about this, see section 15.12 of the JLS:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12

In particular, see 15.12.2.5 Choosing the most specific method:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#301183

/gordon

--
 
G

Gordon Beaton

No, the most specific one is called in each case, so both your methods
are used in this example and the rest of your questions are moot.

Sorry I misread, and see now that you declared both reference types as
Fruit (which I conveniently cut from my response). Still, have a look
at JLS 15.12.

/gordon

--
 
P

Philipp

Hello
I have a somewhat naive question about OO.

If you have two classes Fruit and Apple extends Fruit. You declare:
Fruit f = new Fruit();
Fruit a = new Apple();
f.eat();
a.eat();

if you call the method .eat() on f, you get the eat() from Fruit and if
you call eat() on a, you get the overriden eat() behavior in Apple.

On the other hand, if somewhere else, the following two methods are
defined:
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

So in one case, the runtime "knows" that the object is actually an Apple
although it was declared a Fruit, and makes a distinction by calling the
correct method. In the other case it does not make the distinction. (yes
I know I could check with instanceof and cast)

Why is this so? Is this a design choice in the language? Or is it for
performance reasons? Are there cases when having it the other way (ie.
chosing the method signature which best fits the arguments) would lead
to UB?

Thanks for your comments
Phil
 
P

Philipp

Gordon said:
No, the most specific one is called in each case, so both your methods
are used in this example and the rest of your questions are moot.

I'm sorry, but this is not what happens in my test SSCCE:

// Fruit.java
public class Fruit {
public void eat(){
System.out.println("Eating a Fruit");
}
public static void makeJam(Fruit f){
System.out.println("Making jam with Fruit");
}
public static void makeJam(Apple a){
System.out.println("Making jam with Apple");
}

public static void main(String[] args) {
Fruit f = new Fruit();
Fruit a = new Apple();
f.eat();
a.eat();

makeJam(f);
makeJam(a);
}
}


// Apple.java
public class Apple extends Fruit {
public void eat(){
System.out.println("Eating an apple");
}
}

--- Output ---
Eating a Fruit
Eating an apple
Making jam with Fruit
Making jam with Fruit


For a long discussion about this, see section 15.12 of the JLS:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12

In particular, see 15.12.2.5 Choosing the most specific method:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#301183

Thanks for the refs, I will look into this.

Best regards
Phil
 
G

Gordon Beaton

I'm sorry, but this is not what happens in my test SSCCE:

You're right, I had failed to notice your declarations when I posted
my original response (I posted a follow-up shortly afterwards).
Fruit f = new Fruit();
Fruit a = new Apple();
f.eat();
a.eat();

The fact that Apple.eat() gets invoked instead of Fruit.eat() is that
the reference type doesn't change anything about the object itself, it
only restricts your view of the object.

So if there were a method specific to Apple, you would be unable to
invoke it through the Fruit reference:

a.makeCider(); // will not compile

Similarly, when you pass 'a' to makeJam() you get Fruit jam, not Apple
jam. By choosing to access the object with a Fruit reference instead
of Apple, you are saying "I choose to ignore the fact that this is an
Apple, I only see generic Fruit here".

/gordon

--
 
O

Owen Jacobson

Hello
I have a somewhat naive question about OO.

If you have two classes Fruit and Apple extends Fruit. You declare:
Fruit f = new Fruit();
Fruit a = new Apple();
f.eat();
a.eat();

if you call the method .eat() on f, you get the eat() from Fruit and if
you call eat() on a, you get the overriden eat() behavior in Apple.

On the other hand, if somewhere else, the following two methods are
defined:
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

So in one case, the runtime "knows" that the object is actually an Apple
although it was declared a Fruit, and makes a distinction by calling the
correct method. In the other case it does not make the distinction. (yes
I know I could check with instanceof and cast)

Why is this so? Is this a design choice in the language? Or is it for
performance reasons? Are there cases when having it the other way (ie.
chosing the method signature which best fits the arguments) would lead
to UB?

It's a design choice of the Java language, inherited from its
predecessors (C++, Smalltalk, and various others). Method virtual
dispatch is only performed against the implicit zeroth argument,
"this"; the remaining arguments' *static* types are used to determine,
at compile time, what the signature of the method to dispatch is.

There are languages that do multiple dispatch; Haskell does it via its
pattern-matching mechanism, but it's by no means the first. Even the
Common Lisp Object System has support for multiple dispatch.

While I have no particular insight into why Java's design uses single-
dispatch from a technical standpoint, I do find it easier to read code
in single-dispatch object systems than code for multiple-dispatch
systems. It's also a much more common system; for a language designed
for The Masses that makes it more likely any random programmer already
understands the method dispatch system before coming to Java.

There are times when multiple dispatch leads to nicer code. The
Visitor pattern is a simplistic way to emulate multiple dispatch in a
single-dispatch language; it's all but unnecessary when you can match
calls to method bodies based on the dynamic types of the arguments,
for example.

-O
 
M

Mike Schilling

Philipp said:
Hello
I have a somewhat naive question about OO.

If you have two classes Fruit and Apple extends Fruit. You declare:
Fruit f = new Fruit();
Fruit a = new Apple();
f.eat();
a.eat();

if you call the method .eat() on f, you get the eat() from Fruit and
if you call eat() on a, you get the overriden eat() behavior in Apple.

On the other hand, if somewhere else, the following two methods are
defined:
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

So in one case, the runtime "knows" that the object is actually an
Apple although it was declared a Fruit, and makes a distinction by
calling the correct method. In the other case it does not make the
distinction. (yes I know I could check with instanceof and cast)

Why is this so? Is this a design choice in the language? Or is it for
performance reasons? Are there cases when having it the other way (ie.
chosing the method signature which best fits the arguments) would lead
to UB?

Java inherits this behavior from C++. Stroustrup explains why C++ does this
in his _The Design and Evolution of C++_, the answer being that trying to
dispatch on multiple types (the type of "this" plus the types of one or more
arguments) is far ore complicated and difficult to do efficiently than
dispatching based on just one type. It can also leads to problems like the
folllowing:

Suppose there are three methods foo(Object, Object), foo(Object, String),
and foo(String, Object). Which should be called, given

Object o1 = "abc";
Object o2 -= "def";
foo(o1, o2);
 
S

Stefan Ram

Philipp said:
you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

Yes. But this might not have to do with »OO«
- it is a property of the programming language Java.
Why is this so?

Because you have chosen Jave as a programming language.
You could have chosen another language with another behavior.
Is this a design choice in the language?

Yes.
Or is it for performance reasons?

An alternative is »multiple dispatch«, which might introduce
both performance problems (I am not sure about this) and also
other problems of call semantics.

http://en.wikipedia.org/wiki/Multiple_dispatch
 
J

Joe Attardi

Philipp said:
Fruit f = new Fruit();
Fruit a = new Apple();
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

As some others have already pointed out, it's because f and a are both
references to Fruit. If you want makeJam(a) to call the makeJam(Fruit)
method, you can explicitly cast it as an Apple:

makeJam((Apple) a);
 
D

Daniel Pitts

Mike said:
Java inherits this behavior from C++. Stroustrup explains why C++ does this
in his _The Design and Evolution of C++_, the answer being that trying to
dispatch on multiple types (the type of "this" plus the types of one or more
arguments) is far ore complicated and difficult to do efficiently than
dispatching based on just one type. It can also leads to problems like the
folllowing:

Suppose there are three methods foo(Object, Object), foo(Object, String),
and foo(String, Object). Which should be called, given

Object o1 = "abc";
Object o2 -= "def";
foo(o1, o2);

There was a thread about this just recently on comp.object

Often times the "workaround" in languages that don't dispatch that way
is to use the Visitor pattern. That way, the object itself is
responsible for the final version of the method invoked, so the
polymorphic behavior is preserved.

In general, I think it is often better to have the Apple handling any
difference in makeJam that might happen simply because the Fruit is an
Apple. Sometimes that solution isn't feasible, but its been my
experience that it /often/ is.
 
G

George Neuner

Java inherits this behavior from C++. Stroustrup explains why C++ does this
in his _The Design and Evolution of C++_, the answer being that trying to
dispatch on multiple types (the type of "this" plus the types of one or more
arguments) is far ore complicated and difficult to do efficiently than
dispatching based on just one type.

Multimethod dispatch isn't difficult and it can be implemented quite
efficiently - but whereas single dispatch can be O(1) or O(d) in the
_d_epth of the class tree, multimethod dispatch is O(pt) or O(ptd) in
the number of _p_arameters, _t_ypes/classes and _d_epths of the class
trees.

The most common approach to multimethod dispatch seems to be a
discrimination tree:

if (param1 is typeA)
if (param2 is typeX)
:
else
if (param2 is typeY)
:
else
:
else
if (param1 is typeB)
:
else
:

If the runtime data is organized properly, it is possible to have O(1)
type checks even with multiple inheritance. It may not be necessary
to check all the parameters - each test must reduce the set of
possible matches (eventually to one) and a parameter which has the
same type in all the remaining overloads has no discriminating value.
And parameters do not need to be checked in order - the tree can be
arranged so that parameters with the most discriminating value are
checked first.

Java and C++ already perform this kind of discrimination to decide
which overloaded function to call, but the decision is made at compile
time using the parameter's static types.

Lisp generates code for multimethod dispatch at runtime (at the first
call) because Lisp allows runtime modification of classes and runtime
redefinition of methods - a new dispatch tree must be constructed for
a multimethod if any of the classes or overloaded methods involved are
changed. However, Haskell and ML, which don't allow runtime
modification of methods or classes construct their dispatch code at
compile time.

Studies of multimethod use have shown that there are only a few
parameters and types involved in a typical dispatch so the
discrimination tree is usually quite shallow.

George
 
M

Mark Space

Philipp said:
On the other hand, if somewhere else, the following two methods are
defined:
public void makeJam(Fruit f){};
public void makeJam(Apple a){};

Meanwhile, on the third hand, if you had declared makeJam() as a method
of both Fruit and Apple, you could invoke it polymorphically.

class Fruit {
makeJam() {
}
}
// Same for Apple
f.makeJam();
a.makeJam();
 
R

Roedy Green

and you call makeJam(f) and makeJam(a), both times it's the
makeJam(Fruit) which is called.

nope. Java will use the most specific overloaded variant that it can
determine at compile time. The restriction is the overloading choice
must be made from information available only at compile time. (In
contrast Nice decides at run time. See
http://mindprod.com/jgloss/nice.html)

However, when you use a method call like this:

a.makeFruit(). Java can decide which version to use at RUN time based
on the type of a.

See http://mindprod.com/jgloss/gotchas.html#OVERRIDE
 

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,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top