Suppose if I have the following class:
public final class Person {
final private String personFirstName;
final private String personLastName;
final private ConcurrentMap<Double, String> phoneMessages;
public Person(String firstname, String lastname) {
phoneMessages = new ConcurrentHashMap<Double, String>();
this.personFirstName = firstname;
this.personLastName = lastname;
}
public void add(Double key, String item) {
phoneMessages.put(key, item);
}
public String getPersonFirstName() {
return personFirstName;
}
public String getPersonLastName() {
return personLastName;
}
}
Even though I created a class with a private final thread-safe collection, is my class immutable? My guess is no.
If having a collection in an object isn't proper practice what is the proper practice in Java? How would I go about designing my class that contains a collection?
As others have pointed out, how you use your class will determine if making it immutable is appropriate.
That said, this version of your Person class is immutable:
public final class Person {
final private String personFirstName;
final private String personLastName;
final private ConcurrentMap<Double,String> phoneMessages;
public Person(String firstname, String lastname) {
this.phoneMessages = new ConcurrentHashMap<Double,String> ();
this.personFirstName = firstname;
this.personLastName = lastname;
}
private Person(String firstname, String lastname, ConcurrentHashMap<Double,String> phoneMessages) {
this.personFirstName = firstname;
this.personLastName = lastname;
this.phoneMessages = phoneMessages;
}
public Person add(Double Key, String item){
ConcurrentHashMap<Double, String> newMap = new ConcurrentHashMap<>(this.phoneMessages);
newMap.put(Key, item);
return new Person(this.personFirstName, this.personLastName, newMap);
}
public String getPersonFirstName() {
return personFirstName;
}
public String getPersonLastName() {
return personLastName;
}
public Map<Double, String> getPhoneMessages() {
return Collections.unmodifiableMap(this.phoneMessages);
}
}
Notice that the add method returns a different instance of Person so that the current Person instance remains unmodified (immutable).
Related
I have created a Person, class and a Professor class that both use the Builder Pattern to create objects. The Professor class takes a Person object as an argument in its constructor. I am trying to use both classes together, but when I attempt to print out a professor, get the following output: null null (instead of Bob Smith).
Here's what I tried so far:
Person:
public class Person {
private String firstname;
private String lastname;
private int age;
private String phoneNumber;
private String emailAddress;
private char gender;
public Person(){}
// builder pattern chosen due to number of instance fields
public static class PersonBuilder {
// required parameters
private final String firstname;
private final String lastname;
// optional parameters
private int age;
private String phoneNumber;
private String emailAddress;
private char gender;
public PersonBuilder(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public PersonBuilder phoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public PersonBuilder emailAddress(String emailAddress) {
this.emailAddress = emailAddress;
return this;
}
public PersonBuilder gender(char gender) {
this.gender = gender;
return this;
}
public Person build() {
return new Person(this);
}
}
// person constructor
private Person(PersonBuilder builder) {
this.firstname = builder.firstname;
this.lastname = builder.lastname;
this.age = builder.age;
this.phoneNumber = builder.phoneNumber;
this.emailAddress = builder.emailAddress;
this.gender = builder.gender;
}
#Override
public String toString() {
return this.firstname + " " + this.lastname;
}
}
Here's the Professor class:
package com.example.hardcodedloginform;
import java.util.List;
public class Professor extends Person{
private Person professor;
private double salary;
private String courseTaught;
private List<Student> students;
private int professorID;
public static class ProfessorBuilder {
// required fields
private Person professor;
private int professorID;
// optional fields
private double salary;
private String courseTaught;
private List<Student> students;
public ProfessorBuilder(Person professor, int professorID) {
this.professor = professor;
this.professorID = professorID;
}
public ProfessorBuilder salary(double salary) {
this.salary = salary;
return this;
}
public ProfessorBuilder courseTaught(String courseTaught) {
this.courseTaught = courseTaught;
return this;
}
public ProfessorBuilder students(List<Student> students) {
this.students = students;
return this;
}
public Professor build() {
return new Professor(this);
}
}
private Professor(ProfessorBuilder builder) {
this.salary = builder.salary;
this.courseTaught = builder.courseTaught;
this.students = builder.students;
}
#Override
public String toString() {
return "" + super.toString();
}
}
And here is the Main class where I try to print out a professor object:
public class Main {
public static void main(String[] args) {
Person profBobs = new Person.PersonBuilder("Bob", "Smith")
.age(35)
.emailAddress("bob.smith#SNHU.edu")
.gender('M')
.phoneNumber("818-987-6574")
.build();
Professor profBob = new Professor.ProfessorBuilder(profBobs, 12345)
.courseTaught("MAT101")
.salary(15230.01)
.build();
System.out.println(profBob);
}
}
I would like the printout in the console to be "Bob Smith", but what I am seeing is: null null. I checked and found that the Person object profBobs is, in fact, created properly and does print out the name "Bob Smith" when I attempt to print it the same way. I don't know why my Professor prints: null null.
Your Professor constructor fails to initialise any member fields of its base class.
There are multiple ways to solve this. One solution has ProfessorBuilder extend PersonBuilder:
public class Professor extends Person {
// Remove the `person` field! A professor *is-a* person, it does not *contain* it.
private double salary;
private String courseTaught;
private List<Student> students;
private int professorID;
public static class ProfessorBuilder extends Person.PersonBuilder {
// required fields
private int professorID;
// optional fields
private double salary;
private String courseTaught;
private List<Student> students;
public ProfessorBuilder(Person professor, int professorID) {
super(professor);
this.professorID = professorID;
}
// …
}
private Professor(ProfessorBuilder builder) {
super(builder);
this.salary = builder.salary;
this.courseTaught = builder.courseTaught;
this.students = builder.students;
}
}
For this to work you also need to mark the Person constructor as protected rather than private.
Furthermore, your Professor.toString method implementation made no sense: it essentially just called the base class method, so there’s no need to override it. And prepending the empty string does nothing.
im trying to learn how to use pattern builder. i could get it to work until i tried to use enum.
I tried to change the code couple of times and each time had different error. right now the error is Incompatible types.
Please can you help bringing this code to working state and if you have suggestions to improve the code it would be great.
thanks.
EDIT:
now it seems to be okay, but how do i use it with the builder inside the main?
this was the code i used
main:
Person person3 = new Person.PersonBuilder("Julliete", "Kaplan" )
.status(); // what should i write here to set the status?
person class
public class Person
{
private final String name;
private final String lastname;
private final int age;
//My enum im trying to use
private Status status;
public enum Status
{
SINGLE ("Single"), MARRIED ("Married"), WIDOWER ("Widower");
private String status;
private Status(String status)
{
this.status = status;
}
public String getStatus()
{
return this.status;
}
}
//builder
private Person(PersonBuilder builder) {
this.name = builder.name;
this.lastname = builder.lastname;
this.age = builder.age;
this.status = builder.status;
}
//GETTERS
public String getName() {
return name;
}
public String getLastname() {
return lastname;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "Person : "+this.name+", "+this.lastname+", "+this.age;
}
//PersonBuilder
public static class PersonBuilder
{
private final String name;
private final String lastname;
private int age;
private Status status;
public PersonBuilder(String name, String lastname) {
this.name = name;
this.lastname = lastname;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public PersonBuilder status(Status status)
{
this.status = status;
return this;
}
public Person build() {
Person person = new Person(this);
return person;
}
}
Don't define another Status enum inside the builder: reuse the one defined in the Person class.
Otherwise, you've got to map from instances of PersonBuilder.Status to instances of Person.Status: they are entirely separate types.
Currently this mapping is trivial: you can use Person.Status.valueOf(personBuilderStatus.name()) - but you have to ensure that you update both at the same time to have identical values (or at least that PersonBuilder.Status maps to a subset of Person.Status), which is an unnecessary maintenance burden going forwards.
I have a DynamoDB table called 'inbox'. In it will be stored messages that are sent to and from users. There will be more than one type of message, and based on the type, data may come along with the message. I have an object UserMessage that maps to the inbox table. While there will be common fields (e.g. sendTo, sentFrom) for all messages, the data will vary in its structure.
Rather than using a Map for this data, the thought occurs that using Java Generics might be a better approach. I have an object annotated with #DynamoDBDocument, and DynamoDBMapper will serialize that to JSON. When the member variable is declared as:
private ContactData data;
indeed, the result is expected. data is serialized to JSON and stored in the data attribute in inbox in the format of ContactData's annotations. However, to get the flexibility desired by using generic types, a call to DynamoDBMapper.save() throws DynamoDBMappingException: Cannot convert T to a class.
Here is the UserMessage class:
#DynamoDBTable(tableName="inbox")
public class UserMessage<T> {
private String toId;
private T data;
#DynamoDBAttribute(attributeName="data")
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
#DynamoDBHashKey(attributeName="toId")
public String getToId() {
return toId;
}
public void setToId(String to) {
this.toId = to;
}
}
And the code for ContactData:
#DynamoDBDocument
public class ContactData {
private String firstname;
private String lastname;
private String email;
#DynamoDBAttribute(attributeName = "firstname")
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
#DynamoDBAttribute(attributeName = "lastname")
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#DynamoDBAttribute(attributeName = "email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
The controller code to set this up is:
UserMessage<ContactData> message = new UserMessage<ContactData>();
ContactData cd = new ContactData();
cd.setEmail("jane#test.com");
cd.setFirstname("Jane");
cd.setLastname("Smith");
message.setToId("test#email.com");
message.setData(cd)
DynamoDB.getMapper().save(message);
I'm fairly inexperienced, and generic types are brand new to me, so I hope I am using them correctly. I think I am. I just think that DynamoDBMapper can't map to the right class in this case.
Thanks
Jeff
Everytime you use a non-standard attribute class you need to provider a marshaller. Here I used a global Object marshaller you can probably use to just JSON-ify anything you'd like.
I personally prefer using static types, each one with a defined marshaller I can reason about, but ymmv.
public class GenericDBEntity<T>
{
..
private T extra;
#DynamoDBAttribute
#DynamoDBMarshalling(marshallerClass=ObjectMarshaller.class)
public T getExtra()
{
return extra;
}
public void setExtra(T extra)
{
this.extra = extra;
}
public static class ObjectMarshaller implements DynamoDBMarshaller<Object>
{
#Override
public String marshall(Object getterReturnResult)
{
return getterReturnResult.toString();
}
#Override
public Object unmarshall(Class<Object> clazz, String obj)
{
return obj;
}
}
}
I want to find students whose gender is female by using streams
Student class
public class Student {
private String first;
private String last;
private int ID;
private Gender gender;
int next=0;
List<Course> courses=new LinkedList<>();
List<Student> students=new LinkedList<>();
public Student(String first, String last, int iD, Gender gender) {
this.first = first;
this.last = last;
ID = iD;
//this.gender = gender;
}
public void enroll(Course c) {
courses.add(c);
}
public void isFemale(){
Student s;
return s.gender=Gender.F;
}
}
enum class for genders
public enum Gender {
M,F;
private Gender gender;
}
main class
public class Main {
public static void main(String[] args) {
List<Student> studentsOfClass=new LinkedList<>();
studentsOfClass.add(new Student("john","smith",01,Gender.M));
studentsOfClass.add(new Student("mick","tayson",05,Gender.M));
studentsOfClass.add(new Student("sara","conor",04,Gender.F));
studentsOfClass.add(new Student("Tana","smith",02,Gender.F));
Course c1=new Course("fiologiya","anna",0234);
Course c2=new Course("mathematics","maria",1134);
Course c3=new Course("phisics","luisa",0534);
studentsOfClass.stream().limit(3).forEach(s->s.enroll(c1));
Collection<Student> femaleStudents= studentsOfClass.stream().filter(Student::isFemale).collect(Collectors.toList());
}
}
You are using the Stream methods correctly, but your isFamele method is wrong. It should return boolean and check the gender of the current Student.
It should be :
public boolean isFemale()
{
return gender==Gender.F;
}
You should also unremark this constructor line - //this.gender = gender; - and probably remove private Gender gender; from the Gender enum.
In addition, you can change the type of femaleStudents from Collection to List<Student>, which is more accurate.
is it possible to create a dynamic ObservableList with relative StringProperty?
For example, using the code below, how is it possible to recreate it dynamically and add new StringProperty if necessary?
private final ObservableList<Record> recordList = FXCollections.observableArrayList();
public static class Record {
private static int trackId;
private final SimpleIntegerProperty id;
private final SimpleStringProperty name;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Record(String name, String lastName, String email) {
this.id = new SimpleIntegerProperty(trackId);
this.name = new SimpleStringProperty(name);
this.lastName = new SimpleStringProperty(lastName);
this.email = new SimpleStringProperty(email);
trackId++;
}
public int getId() {
return this.id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return this.name.get();
}
public void setName(String name) {
this.name.set(name);
}
public String getLastName() {
return this.lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return this.email.get();
}
public void setEmail(String email) {
this.email.set(email);
}
}
You can't add fields to class even if will use Reflection. Maybe is better to change architecture? Let's see next class:
class Property <T> {
private final T propertyValue;
private final String propertyName;
public Property (String name, T value) {
this.propertyName = name;
this.propertyValue = value;
}
public T getValue(){
return propertyValue;
}
public String getName(){
return propertyName;
}
}
This class helps you to create new property and store it. Now, you can create List of properties and store it in class Record. And now you can dynamically add new properties. It is more flexible in my opinion.