4

Effective Java - Item-2 states ,

a JavaBean may be in an inconsistent state partway through its construction.

I could not understand this, If an object is being constructed in a method, how would that go inconsistent, if exception has to occur, that can occur in constructor too. And how is this related to threading?

codingenious
  • 8,385
  • 12
  • 60
  • 90
  • I just dropped the kindle when reading that particular sentence in the book and googled my way here pretty fast. I believe while the scope of concern is not solely about threading, it would be more common to observe this behavior when constructing objects used by multiple threads, unless you make sure no one pokes the construction especially in between setters. – Kerem May 22 '15 at 23:39

2 Answers2

4

The following bean was presented in the book:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

of these, the servingSize and servings set by setServingSize(int) and setServings(int) are fundamental - at least in the book - for the nutrition facts.

But if you just call:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setCalories(100);

then the servings field would not be set. Thus the resulting instance is now in an invalid state.

So if you would call a method that expects a valid instance, say healthRiskCalculator.calculateHealthRisk(NutritionFacts facts) then you would get an exception either within the object or in healthRiskCalculator.

So now you could check the damage to your health when you call calculateHealthRisk() but there may be many methods that read or use the object instance. Furthermore, you may have created a lot of invalid instances for other products as well. In other words, this is not fail fast.

So there is no fail safe way of creating a constructed object using beans. This is not particular to threading, you can create an invalid bean instance within a single thread.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    Was going through this again and it got me thinking. What if I provide a constructor with mandatory parameters? I guess code will divert from JavaBeans pattern but it will still work, right? – codingenious Mar 25 '17 at 07:10
  • Of course, when I make instances I do this all the time. If the constructor gets too complicated then you could use factory methods or even object factories instead. – Maarten Bodewes Mar 25 '17 at 11:19
  • Agree with Batty on that, a constructor with the mandatory parameters followed by setter calls for optional ones should work just fine as well. – Prateek Jassal Jul 23 '17 at 12:53
  • @PrateekJassal I'm certainly not against such a constructor for mandatory parameters. But then again, this answer mainly explains why a **bean** can be in an inconsistent state. Beans have empty constructors so that the properties can be set later. – Maarten Bodewes Jul 23 '17 at 17:19
1

Imagine a threaded model, where ThreadUtils.doFoo(FooBean fb) would spawn a thread that would work with fb. fb could be as simple as parameters to be passed to that thread to define its computation. Now, take the following constructor:

public FooBean(int i, int j, int k, int l, int port){
    ThreadUtils.doFoo(this);
    someListField = Arrays.asList(i, j, k, l);
    this.port = port;
}

This causes this to leak out of the constructor. If for some spurious reason the spawned thread acted before the list was properly instantiated and assigned, you'd have a thread working on an inconsistent state of this. If, for instance, port was used by the new thread to listen on a socket, and this got leaked, the socket may listen on port 0 (default value of a numeric field), and not on port port.

In fact, an exception in the constructor can be an issue if this is leaked and gets a reachable strong reference from elsewhere.

However, the following constructor is safe:

public FooBean(int i, int j, int k, int l, int port){
    someListField = Arrays.asList(i, j, k, l);
    this.port = port;
    ThreadUtils.doFoo(this);
}

That is because in the Java memory model, the fields are stored before the thread even gets spawned.

nanofarad
  • 40,330
  • 4
  • 86
  • 117
  • 1
    @owlstead Perhaps because the question asks "And how is this related to threading?" – Matt Coubrough Jun 01 '14 at 00:14
  • @MattCoubrough The chapter is suspiciously void of threading in itself. Furthermore, the bean example focuses on the setters, which are not present in hexafractions example. – Maarten Bodewes Jun 01 '14 at 00:17