aspose file tools*
The moose likes Java in General and the fly likes Proper Structure Question / Inheritance & Casting Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Proper Structure Question / Inheritance & Casting" Watch "Proper Structure Question / Inheritance & Casting" New topic
Author

Proper Structure Question / Inheritance & Casting

Idan Gazit
Greenhorn

Joined: Apr 08, 2004
Posts: 5
Hey everybody,
This may belong in the "basic" forum, so feel free to point out that I should skedaddle over there....
My problem is as follows: I'm working on a program which communicates (with external hardware) by means of messages. I've generalized the messaging API I've come up with since there is more than one possible transport layer to my program, but the protocol is identical for all transport layers. So:
class BasicMessage
class MsgFoo extends BasicMessage
class MsgBar extends BasicMessage
class MsgBaz extends BasicMessage
Most functionality is in BasicMessage, which is wholly generic. The various Msg* classes override and extend where they need to for their purposes -- this isn't a place for an interface.
For sending and receiving messages, I've got a "hardware" class which looks something like (pseudocode for brevity):
class Device {
...
public void writeMessage(BasicMessage msg) { ... }
public BasicMessage readMessage() { ... }
}
Now for the problem:
Writing is no problem:
myDevice.writeMessage(new MsgFoo(arg1, arg2, arg3));
Reading, however:
BasicMessage msg = myDevice.readMessage();
MsgFoo mfoo = (MsgFoo)msg; // ClassCastException
So, *I* know that the msg which comes back happens to be a MsgFoo. But at runtime, this fails with a ClassCastException. I thought casting was supposed to allow exactly this kind of behavior, when the programmer knows that a parent class instance is really also a child class instance in disguise.
The creators of java worked around this by adding toXXX() methods for a lot of their code (Integer.toString(), etc... ), but I have many many MsgXXX classes so this would be unweildy and ugly. Redoing BasicMessage as an interface is also no-go, there is a great deal of shared functionality and the whole point of MsgXXX classes is to keep small message-specific info in separate classes with BasicMessage holding the generic guts.
So what do I do? I am at a loss for the "elegant" way around my problem. In the end, what I want to be able to do is to read and write messages, and treat read messages as specific MsgXXX's when I want to....
Thanks!!!
Idan
Maulin Vasavada
Ranch Hand

Joined: Nov 04, 2001
Posts: 1871
Hi Idan
What do you get when you try to do,
System.out.println("Message type:"+msg.getClass().getName()); after line,
BasicMessage msg = myDevice.readMessage();
If that returns you MsgFoo you MUST be able to cast it to MsgFoo...
Regards
Maulin
Michael Couck
Ranch Hand

Joined: Nov 15, 2003
Posts: 46
Hi,
Perhaps you can use the "instanceof" comparator, like

The you won't get a class cast exception, you can decide to use
whatever methods after you know what type of object it is.
Michael
Idan Gazit
Greenhorn

Joined: Apr 08, 2004
Posts: 5
Michael:
I would certainly rather not create a huge if/else block since I have about 150-200 MsgXXX classes! This also sort of defeats the purpose of inheritance. I have a parent class which I know is a child!
With regards to casting, the rule is that you can't cast down unless what you're casting was originally something further down. For instance:

I believe I have all that correct -- I'm not near a compiler so I can't check it, but that's the grasp I have of things now.
One solution I have thought up is the following, but it strikes me as an inelegant solution:
instead of:

I could do:

Since casting up is automatic, that means I could do:

but this introduces other subtle problems that are too long to explain without showing you all of my code. However, those problems are minor, so it is a *workable* solution, but I am convinced there is some more elegant and correct way to do this. Perhaps I need to restructure my classes somehow, but I feel like I'm missing something basic...
Maulin:

What do you get when you try to do,
System.out.println("Message type:"+msg.getClass().getName()); after line,
BasicMessage msg = myDevice.readMessage();
If that returns you MsgFoo you MUST be able to cast it to MsgFoo...

Although I haven't tried it, I am certain that the message type will be BasicMessage. The code inside readMessage looks like this:

which means that anytime I call:

I am bound to get a bonafide BasicMessage back. My problem is that I thought I could do:

but I can't, because the value returned from readMessage was never a MsgFoo to begin with, so I can't cast down from BasicMessage.
Still frustrated.... I have the sneaking feeling that this is what generics (AKA C++'s "templates") are intended to solve and I'm stuck.
Please tell your friends to take a look at this if you're stumped, I really need to figure this out to get my code working and this is all that is stopping my project from progressing right now!
Thanks again for all of the input, and TIA for those now joining in...
Idan
Stephen Suen
Ranch Hand

Joined: Oct 30, 2003
Posts: 34
Hi, All,
I have the same problem.
I have a structure in the following pattern:

Then, the problems come out. I have so many if/else full of readMsg, further
more, BasicMsg must has knowledge of all XXXMsg, so it can construct them! I think this kind of coupling cannot be accepted.
I'm wondering whether there is some design pattern or best practice to do with.
Any help will be appreciated.
Stephen Suen
Maulin Vasavada
Ranch Hand

Joined: Nov 04, 2001
Posts: 1871
Hi Idan

but I can't, because the value returned from readMessage was never a MsgFoo to begin with, so I can't cast down from BasicMessage.

well, that explains it. if we do "new BasicMessage()" how can we say it is of MsgFoo type?
We can do following though,
public BasicMessage readMessage() {
BasicMessage msg = new MsgFoo("something here");
return msg;
}
and then we can cast it to MsgFoo ...
So bottomline if you really don't have MsgFoo object you can't cast it to that type.
I guess it makes sense...
Regards
Maulin
Dirk Schreckmann
Sheriff

Joined: Dec 10, 2001
Posts: 7023
So, *I* know that the msg which comes back happens to be a MsgFoo. But at runtime, this fails with a ClassCastException.
Uh uh. You must be wrong, because I doubt that the JRE is. Your message coming back must not be of type MsgFoo, if a ClassCastException is being thrown when casting the reference to type MsgFoo.
What does a quick call to msg.getClass() return?
System.out.println(msg.getClass());
It's probably not "class MsgFoo".


[How To Ask Good Questions] [JavaRanch FAQ Wiki] [JavaRanch Radio]
Eddie Vanda
Ranch Hand

Joined: Mar 18, 2003
Posts: 281

BasicMessage msg = new BasicMessage(myRawByteStream);
return (msg);

Surely your fix needs to be in this area. There has to be something in your raw byte stream or in your request for it which indicates which subclass this has to be and where you should do the proper sub-class casting. Or are you serialising/deserialising?


The nice thing about Standards is that there are so many to choose from!
Idan Gazit
Greenhorn

Joined: Apr 08, 2004
Posts: 5
Hey again,
First off, a bit of clarification, followed by responses:
I know that as far as the JRE is concerned, my message object never was a MsgFoo object to begin with, hence the class cast exception. Perhaps I have framed my question wrong, so let me clarify:
1. All message objects are identical at the data level (they all inherit from BasicMessage).
2. The only thing that a MsgXXX does that a BasicMessage doesn't is a set of functions to deal with the byte stream more intelligently given what I know of that class of messages. It does not add members (well, it adds one static final, a message number for that class of messages).
The underlying byte stream is the same for BasicMessage and also all MsgXXX classes. In fact, all of the underlying stuff in MsgXXX is inherited from BasicMessage.
An example to clarify: MsgFoo is a message about the number of foo's in my widgetron. A message is always 10 bytes, the latter 5 being the payload. The first five are standard across all messages (start byte, checksum, message number, etc...).
So, MsgFoo's stipulate that bytes 1-4 of the payload (0-based) are a little-endian integer value which represents the number of foo's in my widgetron. I could do something like:

which would work for any message, but is cumbersome. Instead, I subclassed BasicMessage into a small forest of classes which allow things like:
myMsgFooInstance.setNumFoos(2);
where the code for setNumFoos() looks like:

See? Basically I subclassed in order to allow more specific interpretation of the same underlying data structure. Perhaps this was not the right way to go?
Since last night I thought that perhaps the subclasses should instead just be static classes which take a basicMessage as an argument to work on? Something like this:

Or something of the like, but it strikes me as another kludge. I must be missing something -- this seems like a too-prevalent situation for there not to be a clean solution....

And now for responses (if I haven't mentioned by name, it's because I feel I've addressed your responses above already):
Dirk:
W/R/T "*I* Know that the message is a MsgFoo" -- The basic thing is that in theory, I could invoke all MsgXXX functions on a basic message -- however, they would provide garbage responses interpretations for messages of a different type. So *I* know that a given BasicMessage happens to be a MsgFoo because I just sent a MsgFoo to my device and I expect one back. The data structure is the same, but the type is different.
So what is now (abundantly) clear to me is that I can't "force" a cast down the tree if my object wasn't originally of the child type.
So, the next question is, why not put all of the various MsgXXX-specific interpretation functions into BasicMessage? Well, it would clutter up the class significantly (150 MsgXXX's * 3-4 interpretation functions per MsgXXX = 450-600 or so functions in BasicMessage). So that's not a real option, plus it sort of defeats the purpose of good OO design and separation.
Eddie:
Yes, there is something in the raw bytestream which indicates which message the BasicMessage is: a message number byte. However, it would force me to write up a 150-200 case long switch or if/else, and maintain that switch every time I invent a new kind of message. Again, defeating the purpose of OO.
What I want is a way to send and receive messages in general fashion, but interpret those messages as whatever message I choose.
No, I'm not serializing/deserializing.
Ok...
Thanks to all for your time!! I sincerely appreciate your assistance and braintime in helping me out with my quandary... Please don't stop posting, I'm sure one of you will eventually smack your forhead and say "this idiot forgot to read page 1 of the java spec," but humor me as I can't seem to dig up a good solution to this on my own.
Thanks again,
Idan
Stefan Wagner
Ranch Hand

Joined: Jun 02, 2003
Posts: 1923

I guess one good solution was, to use

but perhaps you may override it in your 150 classes to:

or you generate a conversion-Method, to make a MsgFoo from a BasicMessage

but somewhere the distinction between the 150 cases must be made.
Perhaps you may get around the writing of 150 methods (or contructors), if your BasicMessage has a convert-method, which uses reflection:

The 7 exceptions you have to catch are omited.


http://home.arcor.de/hirnstrom/bewerbung
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Sorry if I didn't read all this carefully enough ... did you say all 150 types have the same data but different behavior? Maybe inheritance is not the answer. Consider adding a behavior class. I have something at work like this:

Now I'm doing all this manipulation oustide the ArgClass because ArgClass comes from a code generator and cannot have custom methods. Maybe your arg could be smarter and manage this internally:

In the books, this is Strategy pattern.


A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Proper Structure Question / Inheritance & Casting