Search code examples
javabuilder-pattern

Java Builder Object Printing Null


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("[email protected]")
                .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.


Solution

  • 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.