java class heirarchy matching?

F

falcon

I have a heirrarchy of classes that is basically used to create a tree
of objects. I then want to 'evaluate' that tree of objects using
functions that are outside those classes (typically OO programmers
would probably make those functions methods of objects in the tree).

It seems to me that the function which matches the object tree is not
matching correctly. Following is a simplified version of my test code:

abstract class Root{
public abstract Root[] getRoots();

public String toString(){
StringBuffer str = new StringBuffer();
for(int i=0;i<getRoots().length;i++)
str.append(getRoots().toString());
return " ("+this.getClass().getSimpleName()+ str+ " )";
}
}

class Leaf1 extends Root{
public Root[] getRoots(){ return new Root[]{}; }
}

class Leaf2 extends Root{
public Root[] getRoots(){ return new Root[]{}; }
}

class LeafPlus extends Root{
private Root a,b;
public LeafPlus(Root ra, Root rb){ a=ra; b = rb; }
public Root[] getRoots(){ return new Root[]{a,b}; }
}

class LeafMult extends Root{
private Root a, b;
public LeafMult(Root ra, Root rb){ a=ra; b = rb; }
public Root[] getRoots(){ return new Root[]{a,b}; }
}

class Calculate{
public static int value(Leaf1 l){return 1;}
public static int value(Leaf2 l){return 2;}
public static int value(LeafPlus l){return
Calculate.value(l.getRoots()[0])+Calculate.value(l.getRoots()[1]);}
public static int value(LeafMult l){return
Calculate.value(l.getRoots()[0])*Calculate.value(l.getRoots()[1]);}

//As I see it, this method should not have to exist, and even if it
does, it should never be called
public static int value(Root l){return 100;}
}

public class TestCases {

public static void main(String argv[]){

//Print out the tree, see the result below
System.out.println(new Leaf1());
System.out.println(new Leaf2());
System.out.println(new LeafPlus(new Leaf1(),new Leaf2()));
System.out.println(new LeafMult(new Leaf1(), new Leaf2()));

//Print out the result of the evaluation, see the result below
System.out.println(Calculate.value(new Leaf1()));
System.out.println(Calculate.value(new Leaf2()));
System.out.println(Calculate.value(new LeafPlus(new Leaf1(),new
Leaf2())));
System.out.println(Calculate.value(new LeafMult(new Leaf1(), new
Leaf2())));
}
}

//Result
(Leaf1 )
(Leaf2 )
(LeafPlus (Leaf1 ) (Leaf2 ) )
(LeafMult (Leaf1 ) (Leaf2 ) )
1
2
200 <= I want the result to be '3' (1+2==3)
10000 <= I want the result to be '2' (1 * 2 == 2)

I have tried some variations such as making the Calculate class
non-static, which still doesn't give the answer I expect. The
"value(Root l)" methods is called from LeafMult and LeafPlus, even
though within those methods the Root objects are correctly recognized
as either Leaf1 or Leaf2 (according to some print statements which I
took out). Any ideas?
 
P

Patricia Shanahan

falcon said:
I have a heirrarchy of classes that is basically used to create a tree
of objects. I then want to 'evaluate' that tree of objects using
functions that are outside those classes (typically OO programmers
would probably make those functions methods of objects in the tree).

Well, you already know the solution to your problem. Java method
selection is designed for OO progamming, and will tend to naturally do
what you need if you write your program that way.
abstract class Root{
public abstract Root[] getRoots();

public String toString(){
StringBuffer str = new StringBuffer();
for(int i=0;i<getRoots().length;i++)
str.append(getRoots().toString());
return " ("+this.getClass().getSimpleName()+ str+ " )";
}
} ....
class Calculate{
public static int value(Leaf1 l){return 1;}
public static int value(Leaf2 l){return 2;}
public static int value(LeafPlus l){return
Calculate.value(l.getRoots()[0])+Calculate.value(l.getRoots()[1]);}
public static int value(LeafMult l){return
Calculate.value(l.getRoots()[0])*Calculate.value(l.getRoots()[1]);}

//As I see it, this method should not have to exist, and even if it
does, it should never be called
public static int value(Root l){return 100;}
}


The problem is that the run time use of object class to pick a method
only applies to the object containing the method, not to its parameters.
The compiler, when processing e.g. the value calls inside the LeafMult
value method, sees that it needs a value() method that can deal with
Root parameter, and selects the last one in Calculate.

Why not add an abstract "getValue" method to Root and put an
implementation in each subclass?

Patricia
 
F

falcon

The problem is that the run time use of object class to pick a method
only applies to the object containing the method, not to its parameters.
The compiler, when processing e.g. the value calls inside the LeafMult
value method, sees that it needs a value() method that can deal with
Root parameter, and selects the last one in Calculate.

I'm not sure I understand. How come the following works:
System.out.println(Calculate.value(new Leaf1()));

Yet the following doesn't:
System.out.println(Calculate.value(new LeafPlus(new Leaf1(),new
Leaf2())));

In both cases, a single parameter Calculate.value is passed an object
which is either Leaf1 or Leaf2. What's more, this is known at compile
time. More surprising is the fact that when I print the type of
objects passed in to value methods, the types are identified correctly.
This is even true when value(Root r) is dispatched...the printed value
is shown as Leaf1 or Leaf2 (so why wasn't value(Leaf1/2 l) dispatched
then? Strange!

Why not add an abstract "getValue" method to Root and put an
implementation in each subclass?

The point of this excercise is to get a better understanding :) I'd
also like to keep a number of different Calculate classes. Basically
I'm trying to apply some lessons from functional programming. Perhaps
the two are better kept seperate.

Thanks Patricia.
 
P

Patricia Shanahan

falcon said:
I'm not sure I understand. How come the following works:
System.out.println(Calculate.value(new Leaf1()));

In this case the compile time type of the parameter, "new Leaf1()" is
Leaf1. That method makes no other calls, so that resolves all the method
selection.
Yet the following doesn't:
System.out.println(Calculate.value(new LeafPlus(new Leaf1(),new
Leaf2())));

The top calculateValue call, with compile time parameter type LeafPlus,
will call Calculate.value(l.getRoots()[0]) and
Calculate.value(l.getRoots()[1]).

The return type for l.getRoots(), with l of type LeafPlus, is Root. The
compiler will set up each of those calls to require a Calculate.value
with a Root parameter. Methods that don't accept any Root reference as
parameter will not be considered at all.
In both cases, a single parameter Calculate.value is passed an object
which is either Leaf1 or Leaf2. What's more, this is known at compile
time. More surprising is the fact that when I print the type of
objects passed in to value methods, the types are identified correctly.
This is even true when value(Root r) is dispatched...the printed value
is shown as Leaf1 or Leaf2 (so why wasn't value(Leaf1/2 l) dispatched
then? Strange!

You are confusing two different issues that Java distinguishes, the
compile time type of an expression, and the class of an object the
expression references at run time. When you say "print the type", you
are actually printing the run time class of the object referenced by an
expression, not the type of the expression.
The point of this excercise is to get a better understanding :) I'd
also like to keep a number of different Calculate classes. Basically
I'm trying to apply some lessons from functional programming. Perhaps
the two are better kept seperate.

It might be best to separate them until you understand how each behaves
in Java separately. There are ways of doing functional programming in
Java, but more awkwardly than in a language designed for that approach.

Generally, I find the best recipe for happy, smooth programming is to
match the programming language to the way you want to program, so that
you are going with the flow of the way the language works, rather than
fighting it.

Patricia
 
J

John Ersatznom

falcon said:
The point of this excercise is to get a better understanding :) I'd
also like to keep a number of different Calculate classes. Basically
I'm trying to apply some lessons from functional programming. Perhaps
the two are better kept seperate.

Not necessarily. Read up on "visitor pattern"...
 

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,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top