How to have custom builder for Object to set few fields? - java

I have a class Data with list and two variable (id and name). I am replacing the variable with list values. First time in the list will be the new values of the id and name. After removing the id and name, the request object will contain only dataList. But the issue is the variable is being used in many places. I am thinking of creating the setter of the id and name properties in the Request object.
I created the getter and but not sure how to create the setter such that I add items to list for the same object. Also, how to override builder for those two fields alone. Assume the request object is big as well.
Class Request
#Builder
#Getter
public class Request {
private String id; //need to removed
private String name; //need to removed
private List<Data> dataList;
public String getId() {
return (dataList != null) ? dataList.get(0).getId() : null;
}
public String getName() {
return (dataList != null) ? dataList.get(0).getName() : null;
}
}
Class Data:
public class Data {
private String id;
private String name;
}
When someone calls setId or setName, then the item at index 0 in the dataList should be updated.
How do I do the same for builder ?

Overriding Lombok's setter doesn't change the builder's behavior, you have to override them both. Let's start with the harder one:
Builder
Lombok's builder is easy to be overridden by defining the skeleton of the builder. The existing parts won't be generated and Lombok only completes the builder. Create athisstatic nested class in Request:
public static class RequestBuilder {
public final RequestBuilder id(final String id) {
this.id = id;
updateData(id, name, dataList);
return this;
}
public final RequestBuilder name(final String name) {
this.name = name;
updateData(id, name, dataList);
return this;
}
}
What is updateData? You need to update the dataList on each builder's method call. The method must be static otherwise the static builder cannot access it. Define it right in the Request class:
private static void updateData(final String id, final String name, List<Data> dataList) {
if (dataList == null) {
dataList = new ArrayList<>();
}
if (dataList.isEmpty()) {
dataList.add(new Data(id, name));
} else {
var data = dataList.get(0);
data.setId(id);
data.setName(name);
}
}
Your case when dataList is null is not handled, so I rather initialize it here for sure (hence the field cannot be final among the formal parameters of the method).
Setter
This is easy, you need to do basically the same thing like in the builder - just override the correct methods:
public final void setId(final String id) {
this.id = id;
updateData(id, getName(), dataList);
}
public final void setName(final String name) {
this.name = name;
updateData(getId(), name, dataList);
}
You are all set. For sake of simplicity, I have annotated the class Data with Lombok annotations #lombok.Data (beware of the name) and #AllArgsConstructor.
Test
It's always a good practice to write at least some units tests to cover and verify the behavior. I needed to annotate Request with #AllArgsConstructor to avoid calling the setters which are subjects of the test. I also would need some helpful methods for assertion and eliminating code duplication:
void assertRequestBeforeTest(final Request request) {
assertThat(request.getId(), nullValue());
assertThat(request.getName(), nullValue());
assertThat(request.getDataList(), hasSize(0));
}
void assertRequestAfterTest(final Request request, final String id, final String name) {
assertThat(request.getId(), is(id));
assertThat(request.getName(), is(name));
assertThat(request.getDataList(), notNullValue());
var data = request.getDataList().get(0);
assertThat(data, notNullValue());
assertThat(data.getId(), is(id));
assertThat(data.getName(), is(name));
}
And the tests:
#Test
void setter_onNullFields() {
var request = new Request(null, null, new ArrayList<>());
assertRequestBeforeTest(request);
request.setId("id-new");
request.setName("name-new");
assertRequestAfterTest(request, "id-new", "name-new");
}
#Test
void setter_onExistingFields() {
var request = new Request("id", "name", new ArrayList<>());
assertRequestBeforeTest(request);
request.setId("id-new");
request.setName("name-new");
assertRequestAfterTest(request, "id-new", "name-new");
}
#Test
void builder() {
var requestBuilder = Request.builder().dataList(new ArrayList<>());
var request = requestBuilder.id("id-new").name("name-new").build();
assertRequestAfterTest(request, "id-new", "name-new");
}

You only need to override the set method and set values within the method
public void setId(String id) {
if (dataList == null) {
dataList = new ArrayList(2);
}
if (null == dataList.get(0)) {
dataList.add(0, new Data());
}
dataList.get(0).setId(id);
}

If many places are calling the Request object. You can replace the setter of the id and name properties in the Request object. When rewriting the setter method of the id and name properties in the Request object, you can call the setter method of the Data object to write

Related

Java How can I have a method return either a child or parent class object

I have two classes where one class inherits the other one as given below:
public class UserData {
protected final String emailAddress;
protected final String name;
public UserData(final String emailAddress, final String name) {
this.emailAddress = emailAddress;
this.name = name;
}
public Optional<String> getEmailAddress() {
return Optional.ofNullable(this.emailAddress);
}
public Optional<String> getName() {
return Optional.ofNullable(this.name);
}
}
public class EmployeeData extends UserData {
protected final String designation;
public EmployeeData(
final String emailAddress,
final String name,
final String designation
) {
super(emailAddress, name);
this.designation = designation;
}
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
}
I need to create method in another class that can return either one of these objects and have all getters accessible. I already tried making the return type UserData for both kinds of objects (example given below) but that way, I cannot access the getDesignation getter for EmployeeData. Is there a better way inheritance can be setup to avoid this problem where I cannot access child-specific properties?
public UserData getData() {
if (...some condition) {
return new EmployeeData("address#provider.com", "myName", "Dev")
}
else {
return new UserData("address#provider.com", "myName");
}
}
I did look into these stackoverflow questions but couldn't quite figure it out for my use case
C# how to make a function that can return either child or parent class
What's the equivalent of C# IEnumerable in Java? The covariant-capable one, not the Iterable
Because the object we are returning is of type UserData, we will be unable to call methods that are added within the child class, EmployeeData. You could create the getDesignation() method inside the UserData class and have it return an empty optional object.
public Optional<String> getDesignation() {
return Optional.empty();
}
In this case, you can now override the method within the EmployeeData class to return designation as an Optional like this,
#Override
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
Now you will have access to the getDestination() method from returned object of getData(), but you will have to be careful and understand that if the returned type is of UserData, then when calling getDesignation() you will be receiving an Optional.empty() object.

"No setter/field for field found on class"

I'm creating an app in Android Studio, which connects to a Cloud Firestore database. In the database I have the following structure:
Myclass
- name = "test"
- subclass
- 0 = "String 1"
- 1 = "String 2"
The class itself is declared like this (irrelevant bits removed):
public class Myclass {
private String name;
private String[] subclass;
// CONSTRUCTOR
public Chart() {}
//GETTERS
public String getName() { return this.name; }
// SETTERS
public void setSubclass(String[] thisSubclass) { this.subclass = thisSubclass; }
}
In the activity, the Myclass object is set up like this (again, irrelevant bits removed):
public class MyclassActivity {
DocumentReference docRef;
Myclass myItem;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up database connection, read in itemId etc...
// ...omitted for clarity...
docRef = databaseRef.collection("myclass").document(itemId);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
myItem = document.toObject(Myclass.class);
}
}
}
}
This reads in the Myclass object, with the name set correctly, but the subclass object doesn't get set up - it's still null.
In the debug console there's the following message:
No setter/field for subclass found on class path.to.app.Myclass
The 'setSubclass' function is greyed out, as if it's never used. I'm sure the problem is something obvious, but I can't see it.
Your problem right now its that your class name must be the same than the constructor. Also you need to add a getter to your subclass parameter.
public class Chart {
private String name;
private String[] subclass;
public Chart() {
//Default empty constructor, required for Firebase.
}
public Chart(String name, String[] subclass) {
this.name = name;
this.subclass = subclass;
}
public String getName() {
return this.name;
}
public String[] getSubclass() {
return subclass;
}
}
In other hand, you don't need to add the setters. They are not required. Firebase will set the value into the field. But you should add them if you're going to interact with the class from outside.
There will be some cases where you want to have different names on your parameters, maybe because you want to follow a camelCase nomenclature or something. If that's the case you can use the annotation #PropertyName to provide a different name in your database and keep your model as you want. For example:
public class Chart {
#PropertyName("name")
private String mName;
#PropertyName("subclass")
private String[] mSubclass;
public Chart() {
}
#PropertyName("name")
public String getmName() {
return mName;
}
#PropertyName("subclass")
public String[] getmSubclass() {
return mSubclass;
}
}
You have two errors in your model class. First one would be the name of the constructor which is different than the name of the class and should be the same. And the second, for the subclass field you have only defined the setter but without a getter.
Your Myclass class should look like this:
public class MyClass {
private String name;
private String[] subclass;
public MyClass() {}
public MyClass(String name, String[] subclass) {
this.name = name;
this.subclass = subclass;
}
public String getName() { return name; }
public String[] getSubclass() { return subclass; }
}
Setters are not not required. If there is no setter for a JSON property, the Firebase client will set the value directly onto the field, that's why is called idiomatic. If you need them explicitly in your code, just add the following setters to your model class like this:
public void setName(String name) { this.name = name; }
public void setSubclass(String[] subclass) { this.subclass = subclass; }
Regarding the use of arrays in the Cloud Firestore database, please see my answer from this post.

I can't get data from model

I created a POJO model and did not call it out.
This is the code to run it.
DataObjectSelectGarage selectGarage = ...;
TextView.setText(selectGarage.getGaragename());
Do not know if it can run this way?
this is model
public class DataObjectSelectGarage implements Serializable {
#SerializedName("Garage_id")
private int id;
public int getGarage_id() {
return id;
}
public void setgetGarage_id(String getGarage_id) {
this.id = id;
}
#SerializedName("Garagename")
private String name;
public String getGaragename() {
return name;
}
public void setGaragename(String name) {
this.name = name;
}
}
public void setgetGarage_id(String getGarage_id) {
this.id = id;
}
The above set method assigning Id = id instead of id= getGarage_id
This cannot work this way
DataObjectSelectGarage selectGarage;
TextView.setText(selectGarage.getGaragename());
As DataObjectSelectGarage is not initialized. You need to initialize it first.
Secondly in code you posted there are multiple logical bugs like you are passing UserId to HTTP request param without assigning value to it. Secondly without pasring response trying to present it to user.
#Override
public void onResponse(String response) {
/*DataObjectSelectGarage dataObjectSelectGarage = new
DataObjectSelectGarage();
String test1 = dataObjectSelectGarage.getGaragename();*/
tvTest.setText(selectGarage.getGaragename());
Log.d("Selectgarage",response.toString());
}
you need to parse the response string and then after initializing model object and setting values in it use it to show data on view.

Can I prefix a function with "get" in Springboot

I have a Mcq class associated to a MongoRepository, and I want to get an instance of my Mcq which apply several changes (Answers shuffle, Questions draw, etc). I declared my function "myMcq.getInstance()", but I can't do that because every time I want to send a Mcq in a ResponseEntity there is an error in the JSON output because Springboot thinks that there is a "instance" property in my class.
Here is my java class :
#Document(collection = "Mcqs")
public class Mcq {
#Id public String id;
#DBRef public User creator;
public String title;
public String categoryID;
public List<McqChapter> chapterList = new ArrayList<>();
public Difficulty difficulty;
public Mcq() {}
public Mcq(String title) {
this();
this.title = title;
}
public ArrayList<String> getQuestionsIDs() {
ArrayList<String> result = new ArrayList<>();
for (McqChapter chapter : chapterList) result.addAll(chapter.getQuestionIDs());
return result;
}
public McqInstance getInstance() {
return new McqInstance(this);
}
}
To prevent the error add #JsonIgnore to getInstance() method:
#JsonIgnore
public McqInstance getInstance() {
return new McqInstance(this);
}
Marker annotation that indicates that the annotated method or field is to be ignored by introspection-based serialization and deserialization functionality. That is, it should not be consider a "getter", "setter" or "creator".

Easy way fetch not null Field values in an object and set to another object of same class

In the below code, I have two objects of same class. One object (A - employeeObjectInDatabase) has all the fields set. Another object (B - employeeObjectForUpdate) has only few of the fields set. basically I need to set not null values of Object B to Object A. Below code has 'if not null' check for each field to set the value. Is there any other better way to get this done?
Is there a better way to replace the code between comments "BLOCK 1 BEGIN" and "BLOCK 1 END"?
In case of classes with few fields, checking for if not null is easy, but in case of 20+ fields there is a lot of if not null check required, hence thought of getting expert opinion.
Sample:
public class Employee {
private String id;
private String name;
private String department;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public static void main(String[] args) {
Employee newEmployeeObject = new Employee();
newEmployeeObject.setId("A100");
newEmployeeObject.setName("Albert");
newEmployeeObject.setName("Physics");
//newEmployeeObject is persisted into database
Employee employeeObjectForUpdate = new Employee();
employeeObjectForUpdate.setId("A100");
employeeObjectForUpdate.setName("Albert Einstein");
//Inorder to update only the fields that are not null
Employee employeeObjectInDatabase = employeeDao.getEmployee();
//BLOCK 1 BEGIN
if (null != employeeObjectForUpdate.getName())
employeeObjectInDatabase.setName(employeeObjectForUpdate.getName());
if (null != employeeObjectForUpdate.getDepartment())
employeeObjectInDatabase.setDepartment(employeeObjectForUpdate.getDepartment());
//BLOCK 1 END
//persist employeeObjectInDatabase as it has the updated information
}
}
You can move the null check in your setter method
public void setName(String name) {
if(name!=null){
this.name = name;
}
}
Then in your main method, you can you simply call the following:
employeeObjectInDatabase.setName(employeeObjectForUpdate.getName());
If in your Data Access layer when you will populate the db values to your object, the same principle will be applied. That is if the Db column value is null, it wont be setting to the property
You could create a member method to transfer non-null field values to a given object of the same type using reflection.
The following shows a solution using org.apache.commons.beanutils.BeanUtilsBean. See if it helps.
Helper in order to copy non null properties from object to another ? (Java)
Add copy method to Object B. This method should copy its non null values to Object A and return it.And also you can use reflection in this method.
look at this post;
Copy all values from fields in one class to another through reflection

Categories