2

Serialization makes sense as an instance method - an object might reasonably be able to serialize itself. An object should only ever be in a valid state, and all valid states of an object should be permissible to serialize. There is nothing invalid about this idea.

But deserialization does not make sense as an instance method. No part of an object's state should have any bearing on the process of constructing another object from data. There is no class foo such that you need a constructed foo in order to construct a foo.

So my question is, does standard java have a pre-existing set of interfaces/facilities to facilitate static deserialization? If you implement the instance-based approach, your deserialization "just works" (as much as anything does) with anything that works with Java's default deserialization ability.

Is there anything built in, to use classes as factories for objects of that class, constructed from serial data? Is there anything in Java I could pass a class to, such that this facility would know to call some static method to deserialize to construct an object from its flat form?

SaburoutaMishima
  • 269
  • 4
  • 15
  • 1
    Have you looked at Java's `ObjectInputStream` class? That's the usual (built-in) way to deserialize a Java object, and it doesn't require a pre-existing instance of the class to be deserialized (just as you'd expect). – neuronaut Jun 09 '15 at 22:45
  • I have. Maybe I'm confused about exactly how Java handles serialization. So, if you want your class to be serializable, you implement Serializable, right? You customize that by implementing readObject and writeObject. Something in java knows to call those instance methods. What I was wondering, is: is there something in Java that would know to call "static Foo readObject" from class Foo? – SaburoutaMishima Jun 09 '15 at 22:48
  • @SaburoutaMishima yes, this is done via [reflection](https://docs.oracle.com/javase/tutorial/reflect/) by the Java runtime. You can find more on this topic [here](http://www.javaworld.com/article/2072752/the-java-serialization-algorithm-revealed.html). And for good measurement: an example of [the `Object[Input|Output]Stream` in use with a custom class](http://stackoverflow.com/a/8586621/4216641). – Turing85 Jun 09 '15 at 22:50
  • Unclear what you're asking. The `readObject()` method is called *after* constructing a blank instance of the class, to fill it in. You may not like it being non-static but bad luck, it is. – user207421 Jun 09 '15 at 23:42

1 Answers1

2

The deserialization instance method readObject is private. There is no way to call it from the outside. You could call it from one of your instance methods, but that would be very strange and I'd question why you'd be doing that in the first place. You say:

No part of an object's state should have any bearing on the process of constructing another object from data.

True, but I don't see why you think this would be an issue. There's no way you could call readObject from the outside (unless you call it from some other public method, which as I said, is kind of iffy) on an instance that you have already created. When you deserialize, you will most probably be using ObjectInputStream, which will use the no-args constructor to create a new instance, and then will hydrate that object using the data from the stream (when you call ObjectInputStream#readObject). So there is no question of the state of the instance affecting deserialization, because what you get back is an instance created from the serialized data (as Object, but you will then cast it to the concrete type).

In effect, readObject behaves somewhat like a constructor, except that it uses previously-serialized data to create an instance of an object. Extending the analogy, your question wouldn't make sense because you would be asking "Why does creating an object using the constructor have anything to do with the state of the instance?". The question of state doesn't even apply because you don't even have an instance! Similarly, state doesn't come into play with readObject because can never* deserialize and create an instance by using an existing instance.

If you want greater control over serialization, you can override readObject and writeObject from Serializable within your class if you want to handle things in a special way. You can exert greater control over how the data is written out by implementing Externizable and providing implementations for readExternal and writeExternal.

In your second question you're wondering what the "something" is that calls readObject. The "something" is reflection; ObjectInputStream will check to see if the class has a readObject method. If you've provided your own implementation, it will call that. Otherwise it will call defaultReadObject (which contains the logic for default serialization).

As far as built-in factories for deserialization, there isn't anything and I haven't really felt a need something since the standard serialization/deserialization approach seems to work well.

If you want more information on this, I suggest taking a look at the serialization specification for a comprehensive and in-depth view of how Java tackles serialization, and specifically Object Input Classes for your particular question.

*The only way state comes into it is if you do something strange like calling the readObject method from some other instance method (which would have to take in an ObjectInputStream), and then you have custom logic that performs deserialization based on the state of the existing instance. In other words, the only way the object's state has any bearing on deserialization logic is if you explicitly write it that way. Again, as I mentioned before, that would be very strange code, with a whole lot of caveats and of minimal value.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
  • 1
    nevertheless, it would be nicer to go through user defined constructors for any new objects. – ZhongYu Jun 10 '15 at 00:05
  • I think the appeal of `readObject` and `writeObject` is that the serialization API ends up being very orthogonal. The other option is to have `public Object(ObjectInputStream in)` for every object, but it doesn't make sense for every object to be serializable. Ok, so then you could make `Serializable` an abstract class that has an abstract `writeObject` also. But now you end up with an API where you use a special constructor for deserialization, and `writeObject` for serialization, which isn't as elegant and orthogonal as `readObject` and `writeObject`. – Vivin Paliath Jun 10 '15 at 00:16
  • Also, if I was using this API I would think "ok, I have `writeObject`, but why isn't there a `readObject`?". It would be surprising, and a surprising API is not good! APIs should follow the principle of "least surprise". I would expect a `readObject` counterpart to `writeObject`, but I wouldn't expect a special constructor as a counterpart to `writeObject`. – Vivin Paliath Jun 10 '15 at 00:17
  • @VivinPaliath That's the answer I needed. I wasn't looking for that answer because I was a little confused about how serialization/deserialization worked. I've read over it extensively, but I just didn't ruminate enough over the fact that it's performed by reflection via private methods. So, of course, it's very well containerized. As to the comments about preference, I'm a bit split. I dislike reflection entirely, but I agree that CTOR+method would be surprising. – SaburoutaMishima Jun 11 '15 at 11:09