I read these 2 statement mentioned below from the book Effective Java
1st
Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction.The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters. Attempting to use an object when it’s in an inconsistent state may cause failures that are far removed from the code containing the bug, hence difficult to debug.
2nd
A related disadvantage is that the JavaBeans pattern precludes the possibility of making a class immutable (Item 15), and requires added effort on the part of the programmer to ensure thread safety. It is possible to reduce these disadvantages by manually “freezing” the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. Moreover, it can cause errors at runtime, as the compiler cannot ensure that the programmer calls the freeze method on an object before using it.
,and I am not able to understand what exactly these 2 statement wants to convey , Can you guys help me out to understand the above statement .
Update
I have read answers(not all) on this post , most of community members suggested me to use Constructor Pattern
but in the same book these lines have said
Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving— and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields.
For this Scenario we use telescoping constructor pattern but
The telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.The reader is left wondering what all those values mean and must carefully count parameters to find out. Long sequences of identically typed parameters can cause subtle bugs. If the client accidentally reverses two such parameters, the compiler won’t complain, but the program will misbehave at runtime
that is why suugested to use JavaBeans
over constructor pattern
The other advantages of JavaBeans include reusability, deployment, and customization that can be archived using JavaBeans. However, there are a few disadvantages of JavaBeans, which are its mutability, which makes it not working with Immutable Objects.
JavaBeans provide default constructor without any conditions or arguments. JavaBeans are serializable and are capable of implementing the Serializable interface. JavaBeans usually have several 'getter' and 'setter' methods. JavaBeans can have several properties that can be read or written.
Beans are seeds from the Fabaceae family, commonly known as the legume, pea, or bean family. They are an affordable source of protein, fiber, iron, and vitamins that offer many health benefits.
JavaBeans are classes that encapsulate many objects into a single object (the bean). It is a java class that should follow following conventions: Must implement Serializable. It should have a public no-arg constructor. All properties in java bean must be private with public getters and setter methods.
Let's see the simplest Java Bean:
class Person {
private String firstName;
private String lastName;
public String getFirstName() {return firstName;}
public void setFirstName(String firstName) {this.firstName = firstName;}
public String getLastName() {return lastName;}
public void setLastName(String lastName) {this.lastName = lastName;}
}
The following code creates instance of Person
an initiates it:
Person president = new Person();
p.setFirstName("George");
p.setLastName("Bush");
As you can see:
Why is this bad? Because our class Person
is not thread-safe and therefore we cannot use it directly in multi threaded environment without thinking about syncrhonization.
Here is an example. Several years ago Barak Obama became the President of the US. How can we express this in code?
p.setFirstName("Barak");
p.setLastName("Obama");
In mutlti threaded envronent the president
object is in wrong state when when setFristName()
had already completed and setLastName()
has not called yet because the object contains "Barak Bush" that is obviously wrong.
What is the solution? Let's make `Person immutable:
class Person {
private final String firstName;
private final String lastName;
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {return firstName;}
public String getLastName() {return lastName;}
}
As you can see there is not way to change either first or last name stored in object. The fields a final
and do not have setters. So, our example looks like:
Person president = new Person("George", "Bush"); // elections..... president = new Person("Barak", "Obama");
Since Person
is immutable we cannot re-use the old instanceo of Person
and change its attribute. We have to create the new instance instead. If president
is volatitle
reference assignment is atomic and therefore the code is thread safe.
Update
The problem of constructors is that they are not flexible. Our example has only 2 parameters. But think about real world when class Person
has probably 20 or more field. In this case creating of such object is pretty verbose.
Moreover some fields can be optional. In this case you will probably want to create several overloaded constructors with different number of parameters. To avoid dupplicate assignemnt code it is common technique to use so called telescopic constructors, i.e. pattern when constructor invokes other constructor. This pattern is good but in some too verbose and hard for modifications.
This just means that there is not ideal solution. Each solution has its own advantages and disadvantages.
BTW the solution that combines advantages of immutable object and flexibility of object creation and initialization is builder pattern.
I will not write all examples that are needed for better understanding of these issues. But I hope that my answer helped you a little bit. Now you have a starting point and can learn this issue using other resources. Good luck.
Let's say we have a Person
bean, which has a firstname
and a lastname
properties:
class Person {
private String firstname;
private String lastname;
public String getFirstname() { return firstname; }
public void setFirstname(String firstname) { this.firstname = firstname; }
public String getLastname() { return lastname; }
public void setLastname(String lastname) { this.lastname = lastname; }
}
For that class to be fully initialized, we need to set both the firstname and the last name. But in this implementation, nothing prevent us from doing something like:
Person paulSmith = new Person();
paul.setFirstname("Paul");
paul.getLastname(); // will not throw an error, but is functionnaly invalid
paul.setLastname("Smith");
In comparison, if you implement this class as:
class Person {
private final String firstname;
private final String lastname;
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() { return firstname; }
public String getLastname() { return lastname; }
}
you ensure than immediately after construction, the class is fully initialized:
Person paulSmith = new Person("Paul", "Smith");
// impossible to call getLastname() before lastname is initialized
I also made that class immutable at the same time: Paul can never change his name. That might or might not be possible depending on the use case, but immutable classes are guaranteed to be threadsafe, which is always a good thing.
Note that I could (and should) also use my constructor to validate that a Person
is always created in a valid state:
class Person {
private final String firstname;
private final String lastname;
public Person(String firstname, String lastname) {
if (firstname == null) throw new IllegalArgumentException("a person must have a firstname");
if (lastname == null) throw new IllegalArgumentException("a person must have a lastname");
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() { return firstname; }
public String getLastname() { return lastname; }
}
Additional note on immutability:
Immutable objects are guaranteed to be threadsafe (always good), and have no chance to change when you do not expect it. This makes it easier to reason about immutable object. Less surprises.
That being said, trying to push for immutable objects all around tends to push you to split data and operations in different classes. It is easy to have immutable objects and "services" that operate on them. But if you go that way, you actually break one of the first principle of object oriented design: "encapsulate data and functions in a single component". Designing your classes to be immutable is a great idea, you just need to make sure you know where to stop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With