I\' have a question about initialization of List in the POJO as it follows the next code:
public class Person {
//other fields...
private List
In my opinion it would be best to handle that in the constructors. If a default constructor is used, initialize the list in the constructor.
public Person() {
friends = new ArrayList<>();
}
If a constructor which accepts parameters is used, let the calling class pass in a list.
public Person(ArrayList<> friends) {
this.friends = friends;//friends
}
If it's a managed bean as you say, you should do this in a method annotated with @PostConstruct
public class Person {
private List<String> friends;
@PostConstruct
public void init(){
friends = new ArrayList<String>();
}
//getter and setter...
}
The practice of doing any initialization in the getter and setter is generally frowned upon within the context of JSF. See Why JSF calls getters multiple times
Also, per the API for @PostConstruct, the contract specifies safety features and guarantees that if an exception is thrown in a method annotated as such, the bean should not be put into service. There are no such guarantees on a plain constructor.
In a managed bean, injection happens immediately after construction. This means that any operations you're carrying out in the constructor cannot depend on any injected resources (via @ManagedProperty
). Whereas in a @PostConstruct
method, you'll have access to all the resources declared on the managed bean
EDIT: It's important to note that there can be only one @PostConstruct
for any @ManagedBean
, so all important initializations should happen in there.
It's also worthwhile to note that, while the @PostConstruct
method is the ideal place to initialize a backing bean variable/List
, there are implications regarding the scope of the managed bean
@RequestScoped
: In a managed bean with this annotation, the method will be called per submit of the JSF view concerned. A @RequestScoped
bean is destroyed and recreated with every request, The implication of this is that depending on your setup, the list initialized in the @PostConstruct
may be reset to empty or default values during each request. Under certain circumstances, conversion errors may occur as a result of the re-initialization of the list mid-JSF request.
@ViewScoped
: In a managed bean with this annotation, you're guaranteed to have the @PostConstruct
method run once, if and only if you're dealing with the same instance of the @ViewScoped
bean. If the viewscoped bean is destroyed and recreated, the @PostConstruct
method will run again.
@SessionScoped
: A bean with this annotation is created once and stays alive until the user's HTTP session ends. In this scenario, the @PostConstruct
method is guaranteed to run once and only once until the bean is destroyed
See also
I would suggest this:
public class Person {
//other fields...
private List<String> friends=new ArrayList<>();
// returns a copy to protect original list
public List<String> getFriends() {
Collections.unmodifiableList(new ArrayList<>(friends));
}
public void addFriend(String> friend) {
this.friends.add(friend);
}
public void addFriends(List<String> friends) {
this.friends.addAll(friends);
}
}
My suggestion, add a null check in the getter:
public class Person {
//other fields...
private List<String> friends;
public List<String> getFriends() {
if (this.friends == null) friends = new ArrayList<String>();
return friends;
}
}
But also notice I have omitted the setter. Instead, in any client code, call like this:
personInstance.getFriends().add("Some Item");
Or if you have a full list to add:
personInstance.getFriends().addAll(someStringCollection);
It depends. Usually first way preferable because you may want to add something to collection later. If you won't know was your collection initialized or not you must check it every time.