*
The moose likes Java in General and the fly likes deserialization and 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 "deserialization and casting" Watch "deserialization and casting" New topic
Author

deserialization and casting

Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
A simple socket server receives objects of various types through an ObjectInputStream. So all objects received, regardless of their original type are received as type Object. I would like to find the most compact way of handling the input - likely simply by overloaded handling methods - one for each original object type.

What's getting in the way is the deserialization process, which at this point seems to require that I know what type of object is being processed before I can direct it somewhere.

What I would like is something simple like:



Where some kind of "getType()" method actually returns the original type / class. I've tried, for example:


But no matter what I try, the result is always the same. getName() returns the correct type, but getClass() always returns Object. So, just above; rtm gets cast to Object instead of it's original type.


Correlation does not prove causality.
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Note that "Class.forName(rtm.getClass().getName())" is just a long inefficient way of writing "rtm.getClass()". You'll get exactly the same result.

OK, here's the deal. No one ever believes this the first time they hear it, but trust me.

The number of different types your receiving code can handle is finite and must be known at compile time. You cannot cast an object to a type you don't know about when the program is compiled. The very concept of a cast involves knowing the name of the type when the code is written.

So the way to solve this is just:



If the number of possible types is large, then the "Strategy map" idea that's been talked about elsewhere would be the best idea. But the strategy method is still going to work the same way:



[Jess in Action][AskingGoodQuestions]
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
OK. I'll admit this is not the first time I've read that casting can only be done at compile time. I've been at this all day. You're absolutely right. It's hard to believe the first time. I absolutely do "know about" all the classes that may be processed by the application. They're all declared types at the receiving end. That's why it's so hard to believe that there isn't some kind of rtm.castTo(rtm.getClass()) method that works at runtime. In fact, I can't figure out how to write the idea without it being ... well look rtm.castTo(rtm.getClass) ... refers to rtm twice ... maybe something like rtm.castTo(rtm.getName) or rtm.getClass().castTo() ... It knows what it is by the name I get from getName .... seems soooo much like this should be possible.

BTW: Thanks for analyzing and simplifying rtm.getClass(), even though I guess I won't be needing it for now.
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Originally posted by Roger F. Gay:
seems soooo much like this should be possible.


Well, yes, but the part that's impossible is declaring a variable of the right type to assign the cast reference to. Think about it:

Object o = getAnObjectOfSomeTypeFromSomewhere();
X x = o.castToRealType();

What should "X" be? See? You don't know what type castToRealType() returns until the program runs, but the variable, you need to declare at compile time. Get it?

Now, I'll tell you something, if you promise not to pursue it until after your current project is done. Java does in fact have something called the reflection API which lets you do exactly the kind of thing you're trying to accomplish: call a method of unknown name on an object of unknown type with unknown arguments, discovering all the unknowns only at runtime. It's not even too hard to use: you ask an object for its Class. You ask the Class for the Method objects that class supports. You ask each Method object for the list of parameter types it wants. You can then build an array holding the parameters and call invoke() on the Method object.

But it's "cheating", and relatively slow, and it should really only be used by things like IDEs and Web containers and other tools that have to do special kinds of magic. Your current project shouldn't be using reflection.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Originally posted by Ernest Friedman-Hill:
Now, I'll tell you something, if you promise not to pursue it until after your current project is done. ...


Too late. I looked at tutorials on reflection several times today wondering if it could solve my problem. I could see that I can call methods on objects of unknown type, but that's not what I want to do. (Maybe later, in another stage of the project - I might want to send objects that the current handler has never seen before and doesn't know how to handle.) I even gave some thought to reflecting on the various handlers .... and ... then what? .... maybe each handler method contains a string that identifies the type it's supposed to process. That could be used to match with getName() (Could be.)

As for the question and whether I get it? No. Actually I don't. At present, I only want the recipient program to handle objects that are predfined, that it knows about - that it is perfectly capable of instantiating on its own. Objects of these types reside on the recipient system as well as the sending system. I guess that's not the issue (otherwise, if (obj instanceof MyType) process ((MyType)obj); wouldn't work either.

I know the object type of the object that's received. At least I can get the name of its type, using getName(). If it's an object of type MyType, getName(obj) returns "MyType" even though the object has been cast to type Object. So, all the information needed seems to be there. X = theClassNamed (obj.getClass().getName())

I don't actually need "X x" in any statement. I intend to use overloading so it would just be handler(obj.castToRealType()); - with cast in past tense - i.e. output is (MyType)obj ... and it finds handler(MyType obj)

The only explanation that I speculatively seem to be swallowing at the moment, is that casting is an operation, like plus or minus. But that just makes me dizzy once again about the fact that Java doesn't have an eval function. I'm more of a JavaScript guy. JavaScript does have eval().

[ August 04, 2007: Message edited by: Roger F. Gay ]
[ August 04, 2007: Message edited by: Roger F. Gay ]
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Note that you're still stuck in the same morass about whether casting is a runtime or compile-time operation. Choosing between overloaded methods is up to the compiler -- it happens at compile-time, not at runtime. But your "castToTheRealType()" method runs (of course) at runtime, so its return value cannot be used to help decide between overloads.

'Member I said how nobody believes this the first time? You're still not believing!
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
If all the objects are under your control, there actually might be a simple solution: use overriding instead of overloading. That is, let all the objects implement a common interface (that you then know at compile time), cast them to that interface and call a common method that is defined in that interface.


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Ernest. Don't get discouraged. I still don't fully believe that time is relative.

Anyway, I'm not having so much trouble believing it - regardless of how much I'd like it not to be true. I realize that people design and build compilers and make decisions about how they work. It's not the same thing as philosophy. So I can't prove it's not so by reasoning about it. It is what it is.

What I'm stuggling with is how to rationalize it. I think it should be able to stand a bit of rationalization because I believe that Java is a pretty good language. The more I learn about it, the better I like it. I've even come a long way in my application in just two weeks. Back in the olden days before I got side-tracked with non-tech duties, it would have taken a team 6 months to do what I've already done, and it would definitely have had a lot of bugs.

Them that thunk Java up gave it a lot of thought, and it's been reviewed and pushed to improvements by a whole cast of characters over a goodly number of years. So, something about this must make sense. Making sense is probably a matter of being consistent with the characteristics of the general "class" (reference: English, not necessarily OOP) it belongs to.

I think I'm onto something thinking about casting as an operation, like plus or minus. Apparently, I can no more decide how to cast something at run time than decide whether a function performs a + b or a - b. The only approach available is to write two functions and the logic that decides which one to call.

It is a real inconvinience for some of the things I'd like to do. I'd like to deal with things very dymanically. The easier I can make changing things on the fly, the better. Of course, some of these things can be done in other languages. I mentioned the eval() function because it can be quite helpful - it's been a joy to use in JavaScript. I can build functions on the fly and execute them. Is there any way to do that in Java?
[ August 04, 2007: Message edited by: Roger F. Gay ]
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Ilja - I'm not ignoring you. I've started studying overriding. It's after 11 pm where I am however: about time to call it a day.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
As an aside, the language feature you are missing in Java is called "multi methods" - having methods act polymorphically on all their parameters. In languages that have that feature (Nice is one that is based on Java), you wouldn't even need to cast, as the runtime type of the object would be used.

I don't know the exact reasons why this feature was omitted from Java, but it's a relatively "new" concept for OO languages (both Smalltalk and C++ don't have it), and I guess at least at the time Java was designed, it wasn't really well understood.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Ilja; OK. I was just about to respond to your earlier post. What you're saying now is the idea of overriding doesn't really solve the problem in Java? I can't see how overriding helps. But maybe there's just a little more to it. Do you need some kind of mapping to modify the return type at run time?

Maybe that seems like an obscure question. It's just where I ended up trying to imagine the details of implementation.

Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Hello World by String!
Hello World by Document!
Hello World by CommandMessage!

Last week I was thinking that I'd support only one message type through step 1 of this project and deal with different message types later. This weekend has been great. Thanks all!
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Since Java 1.5

java.lang.Class<T>.cast()

public T cast(Object obj)
Casts an object to the class or interface represented by this Class object.

Parameters:
obj - the object to be cast
Returns:
the object after casting, or null if obj is null
Throws:
ClassCastException - if the object is not null and is not assignable to the type T.
Since:
1.5
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
But this returns generic object:

Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
forName
public static Class<?> forName(String className)
throws ClassNotFoundExceptionReturns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
For example, the following code fragment returns the runtime Class descriptor for the class named java.lang.Thread:

Class t = Class.forName("java.lang.Thread")
A call to forName("X") causes the class named X to be initialized.


Parameters:
className - the fully qualified name of the desired class.
Returns:
the Class object for the class with the specified name.
Throws:
LinkageError - if the linkage fails
ExceptionInInitializerError - if the initialization provoked by this method fails
ClassNotFoundException - if the class cannot be located
Garrett Rowe
Ranch Hand

Joined: Jan 17, 2006
Posts: 1296
But this returns generic object:...

As does this:

What you seem to refuse to accept is that static methods are bound at compile time, always. There are no special cases where a static method is bound at run time. So for an overloaded static method, the compiler always uses the compile time type of the method parameters determine which version of the overloaded method to call.


Some problems are so complex that you have to be highly intelligent and well informed just to be undecided about them. - Laurence J. Peter
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Output:

This java.lang.String is not a java.lang.String
This ToadClass is not a ToadClass


Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Originally posted by Garrett Rowe:
What you seem to refuse to accept is that static methods are bound at compile time, always. There are no special cases where a static method is bound at run time. So for an overloaded static method, the compiler always uses the compile time type of the method parameters determine which version of the overloaded method to call.


I sometimes post to the beginners forum rather than here, even if I think the topic is intermediate; because I'm a beginner in some ways.

Are you saying that the code above could work if I take it out of a static method public static void main() -- or are you saying that casting is a static method .... ??? .... so there's just no way to do it.

Honestly - you should see that my confusion isn't just a result of personality. There is a cast() method in Java (since v1.5), and I can get the Class from Class.fromName(className), and I can get the class name.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
javax.lang.model.element

asType
TypeMirror asType()Returns the type defined by this element.
A generic element defines a family of types, not just one. If this is a generic element, a prototypical type is returned. This is the element's invocation on the type variables corresponding to its own formal type parameters. For example, for the generic class element C<N extends Number>, the parameterized type C<N> is returned. The Types utility interface has more general methods for obtaining the full range of types defined by an element.


Returns:
the type defined by this element
See Also:
Types
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Garrett - I'll tell you what's convincing. Remove the overload for Object obj and you get the following compiler errors.

...\rt>javac MyClass.java
MyClass.java:14: cannot find symbol
symbol : method isWhat(java.lang.Object)
location: class MyClass
isWhat(Class.forName(obj.getClass().getName()).cast(obj));
^
MyClass.java:17: cannot find symbol
symbol : method isWhat(java.lang.Object)
location: class MyClass
isWhat(Class.forName(obj.getClass().getName()).cast(obj));
^
2 errors

Of course; I suppose I still could wonder if I'm just doing it wrong - i.e. that I've somehow constructed argument so that it can only return Object, and it's just something that needs to be fixed.

[ August 06, 2007: Message edited by: Roger F. Gay ]
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Apparently, the cast() method is just a functional alternative for doing exactly the same thing as any explicit cast.

Output:

This String is the string I wrote.
This ToadClass object is a toadie.

Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
JavaRanch Saloon discussion in Dynamic Casting from 2003
http://www.javaranch.com

Illja - Dynamic casting may not solve a problem any more than high level languages solved a problem. You can do everything you need in machine code.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Garrett Rowe:

So for an overloaded static method, the compiler always uses the compile time type of the method parameters determine which version of the overloaded method to call.


That's true for overloading in general, that is, also for non-static methods.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Roger F. Gay:

Illja - Dynamic casting may not solve a problem any more than high level languages solved a problem. You can do everything you need in machine code.


I don't understand what you are getting at. If it seems valuable to you, please elaborate.
Garrett Rowe
Ranch Hand

Joined: Jan 17, 2006
Posts: 1296
Are you saying that the code above could work if I take it out of a static method public static void main() -- or are you saying that casting is a static method .... ??? .... so there's just no way to do it.

I'm saying that there is no way to do what you are trying to do in Java. You'll just keep spinning your wheels until you accept that. There is nothing magical about the Class.cast() that will change this.

Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Thanks for your patience Garrett. Is there any way to make a return type from a method variable using T <T> kinds of things? I'm just thinking it might be nice to bury my "if (obj instanceof Type)" statements in a method. It's not really important to my application (it wouldn't reduce or speed the code in any way) but just one of those learning questions really.
[ August 06, 2007: Message edited by: Roger F. Gay ]
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

There's no way to non-invasively bury the "instanceof" calls, other than the trivial case of putting each one in an "boolean isAString(Object)" kind of method.

But if you can change the classes in question -- i.e., if you're not using Strings, but user-defined types, then there are two ways to do what you're trying to do, but elegantly.

The first is using polymorphism, as has been explained. Add a "doTheRightThing()" method to the base class, case the object you read from the OOS to BaseClass, call doTheRightThing(). Implement this method in each subclass to do the right thing.

The other way to do this is to use the Visitor pattern. It's still polymorphism, but it's a way to keep task-specific code out of those subclasses and in a central place instead. The Visitor pattern is overused, I think.

But as long as you keep "String" and other API classes as some of the alternatives, then the "X instanceof Y" if statements (or the equivalent Strategy map idea) is your only alternative.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Hi Ernest. It's Monday during normal working hours and I'm about to throw you a curve ball. My mind has shifted priority from learning things to doing things.

I'm looking at the server thread where my if (obj instanceof ... statements are and can see that the run() method is getting too complicated. I won't feel that I'm properly maintaining maintainability without simplifying it. Do either of the approaches you mention have distinct advantages over declaring shared references globally and simply hiding all the processing code in additional methods?

i.e. as if I no longer care about the other questions I've asked. I just want to know whether I should jump into the study of polymorphism during working hours this week.
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Using polymorphism is the best solution because you can extend the set of types that can be processed without editing the centralized code; just defining a new subclass, and dropping the class file at the client and server ends would be enough.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Wait a minute. I think I'm on the verge of a really smart idea; but I'm not quite sure yet whether it's exactly what you're telling me. Please explain what you mean by "dropping the class file at the client and server ends."

Maybe it's just a little too early in the morning for me to make an obvious inference. I'm only half way through my first cup-a-java.

Addition by edit:

OK. First cup got cold. As I started sipping on the second; it occurred to me that I could get clarification by reading your earlier post again. Sure enough - the ideas must have stuck. "My" good idea is actually exactly what you're suggesting.

I know polymorphism is basic to OOP and not a new idea - but still - COOL!

Question: When I send a subclass, won't the parent class automatically be sent along with it? Or can I combine the benefits of having processing methods in usable classes (defined in a parent class) while reducing the amount of data being transferred (in the child class)? It could be nice, for example, to add get and set methods this way.
[ August 06, 2007: Message edited by: Roger F. Gay ]
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Serialization sends the data of an object, but not the code. The serialized data contains information about what class the data belongs to, and the version of that class, but the actual .class files are distributed in some other way. Either the class files have to be physically installed at both ends, or you can be using some kind of "class server" mechanism that lets you use a Web server to serve class files for the client to download (RMI has something like this, or applets, or Java WebStart.) But some class files are an integral part of the application itself and have to be downloaded or installed first; the base class of your various child classes would be one of those. Since the application knows the name of that base class, it needs that class file to get started.

And yes, polymorphism is very cool!
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Yipeeee!

You mean --- not only can I have get and set methods in a parent class -- but could also add them to the class that's being serialized -- and it wouldn't increase the actual amount of data being passed ???

(At present, all classes exist at both ends.)

Yipeee!

I felt slightly disgruntled this morning, thinking about the fact that I have default values initialized in my data object contructors -- suddenly occurred to me that deserialization requires a no-arg constructor - is it over-writing non-default values that are set? The Java Programming Language (book) said one thing and then kind of said something like it might have been something else. It didn't take long to test that though - no it's not. (Yipeee!!)

Today has been filled with SOAP. As soon as I finish that and some RDF stuff, I'll have one of the "this release" application layers finished. The next layer will be more intense in its application logic challenges; i.e. I'll be mostly above the lower level processes.
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

Originally posted by Roger F. Gay:

You mean --- not only can I have get and set methods in a parent class -- but could also add them to the class that's being serialized -- and it wouldn't increase the actual amount of data being passed ???


Yes. You could add a thousand methods, and it wouldn't change the data size by a single byte.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Couple questions. I've read all over the internet, that a serializable object must have a "no-arg" class constructor. Perhaps my first mistake was to create an object with a constructor that had arguments, and ran it through. Imagine my surprise when I learned that was unacceptable. But I'm on page 407 of The Java Programming Language, and there's an example there with constructor public Name(String name) ... Do I not understand what a "no-arg" class constructor is? Is the internet info out of date? (Third Edition, published in 2000 - is the book out of date?)

The other thing is (and I think this book is horribly written by the way), I don't understand the difference in treatment between transient, static, and other variables in deserialization. Does deserialization work to preserve the meaning of those terms, even in a foreign context? ... i.e. on the recipient end?
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

For your first question: despite anything you may have read, a Serializable class does not need a default constructor. In my copy of JPL, it says, right before that Name example, that a Serializable class's superclass must either have a no-arg constructor, or must itself be Serializable. For the actual class of the object to be serialized, there's no restriction on constructors, but the class must implement Serializable.

For your second question: "transient" means, precisely, "don't include this member when you serialize instances of this class." Transient members are initialized to their default values in newly deserialized objects.

"static" members belong to the class, and not to any object. When you serialize an object, the static members of that class are not serialized, but you wouldn't expect them to be, would you?
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
OK; found an explanation of the "no-arg" requirement that makes sense. There are apparently a few people out in the world who think a no-arg constructor is always required, and unfortunately wrote about it. But here's what Sun says:

Sun Java 1.5

"To allow subtypes of non-serializable classes to be serialized, ...."

When a serialized object is initialized using its own constructor, does it still appear to be of type Object,

Object obj = in.readObject();

or would you have to:

X x = in.readObject();
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Never mind question above. It comes in as Object obj.
Roger F. Gay
Ranch Hand

Joined: Feb 16, 2007
Posts: 367
Originally posted by Ernest Friedman-Hill:
the static members of that class are not serialized, but you wouldn't expect them to be, would you?


Based on today's work; no, I wouldn't want them to be - not automatically anyway. That would have the effect of changing the "static" part of the state of the recipient system. I would really want to be intentional about a thing like that.

Originally posted by Ernest Friedman-Hill:
Now, I'll tell you something, if you promise not to pursue it until after your current project is done. Java does in fact have something called the reflection API...


Same project - different part. I decided to use reflection to transfer data from custom objects to XML. A simple loop creates an XML tag for each public field in any object. No need to know the fields in advance. Very nice.
[ August 08, 2007: Message edited by: Roger F. Gay ]
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: deserialization and casting