I have a superclass with 3 constructors and I want to know if there is a smarter way to write subclass constructors
public class Person{
private String name;
private int age;
private String homeTown;
public Person(String name){
this.name = name;
this.age = 18;
this.homeTown = "Atlanta";
}
public Person(String name, int age){
this.name = name;
this.age = age;
this.homeTown = "Atlanta";
}
public Person(String name, int age, String homeTown){
this.name = name;
this.age = age;
this.homeTown = homeTown;
}
I also have a subclass that inherits superclass
public class Student extends Person{
private double avgGPA;
private int ID;
private String[] classes;
public Student(double avgGPA, int ID, String[] classes, String name){
super(name);
this.avgGPA = avgGPA;
this.ID = ID;
this.classes = classes;
}
public Student(double avgGPA, int ID, String[] classes, String name, int age){
super(name, age);
this.avgGPA = avgGPA;
this.ID = ID;
this.classes = classes;
}
public Student(double avgGPA, int ID, String[] classes, String name, int age, String homeTown){
super(name, age, homeTown);
this.avgGPA = avgGPA;
this.ID = ID;
this.classes = classes;
}
My subclass works fine and runs without an error, but I want to know if there is another way to write a constructor for the subclass without writing the same constructor 3 times, just because the super class has 3 different constructors.
Well, there is something in Java to simplify your superclass. You can invoke another constructor in the same class using this();. So, instead of setting each variable for each constructor, use one variable-setting constructor and use this(); to pass it defaults. For your superclass, you could use these instead:
public Person(String name){
this(name, 18, "Atlanta");
}
public Person(String name, int age){
this(name, age, "Atlanta");
}
public Person(String name, int age, String homeTown){
this.name = name;
this.age = age;
this.homeTown = homeTown;
}
For the subclass, I'd create a private method called setVars which takes in the three variables you'd use: double avgGPA, int ID, and String[] classes. So, instead of setting them in each constructor, your class could look like this:
public Student(double avgGPA, int ID, String[] classes, String name){
super(name);
setVars(avgGPA, ID, classes);
}
public Student(double avgGPA, int ID, String[] classes, String name, int age){
super(name, age);
setVars(avgGPA, ID, classes);
}
public Student(double avgGPA, int ID, String[] classes, String name, int age, String homeTown){
super(name, age, homeTown);
setVars(avgGPA, ID, classes);
}
private void setVars(double avgGPA, int ID, String[] classes) {
this.avgGPA = avgGPA;
this.ID = ID;
this.classes = classes;
}
I think that's about as efficient as you'd get, unless you want to create a static initialization method as QueenSvetlana's answer recommended.
Something along the lines of this:
public final class Person{
private final String name;
private final int age;
private final String homeTown;
private double avgGPA;
private Person(String name, int age, String homeTown, avgGPA){
this.name = name;
this.age = age;
this.homeTown = homeTown;
this.avgGPA = avgGPA;
}
public static Person createPerson(String name, age, homeTown, avgGPA){
return new Person(name, age, homeTown, avgGPA);
}
public static Person createPersonwithoutHomeTown(String name, age,avgGPA){
return new Person(name, age, "Atlanta", avgGPA);
}
public static Person createPersonwithoutAge(String name,avgGPA){
return new Person(name, 18, "Atlanta", avgGPA);
}
}
Immutable objects are objects that don't change their state after creation, and don't allow for sub classing. In the long run, immutable classes are favorable.
I think modifying your Person object to use a builder-pattern would help you.
Related
I am supposed to refactor duplicated attributes in Student class. I have Student and Professor classes as below. I am really confused about how to do refactoring with attributes. Should I add a new class, or made modifications in one of the classes. If so, how? I could not understand how to proceed with this to-do.
private final String matrNr;
private final String name;
private final int age;
private int semester;
private final String email;
public Student(String name, int age, String email, String matrNr, int semester) {
this.matrNr = matrNr;
this.name = name;
this.age = age;
this.semester = semester;
this.email = email;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getSemester() {
return semester;
}
public String getMatrNr() {
return matrNr;
}
public void increaseSemester(){
semester = semester + 1;
}
}
And the professor is a like:
private final String persNr;
private final String name;
private final int age;
private final String email;
public Professor(String name, int age, String email, String persNr) {
this.persNr = persNr;
this.name = name;
this.age = age;
this.email = email;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPersNr() {
return persNr;
}
}
Thanks for any kind of helps!
Your goal is to refactor duplicated attributes in the Student and Professor classes. The way to do this is to create a parent class which defines the common attributes (like "name"), and modify Student and Professor classes to extend the common parent class. In this way, both Students and Professors can have a "name", even though you have defined "name" only once in the common parent.
Below shows how you could do this with a common "Human" parent class, how the constructors would work, and how you could define a Student-only attribute (semester).
Here is a simple version a common Human class:
common "Human" class
each Human has a "name"
the name is set in the constructor (so when you're creating an object) and cannot be changed later ("name" is final; also no "setHuman()")
class Human {
private final String name;
public Human(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Here's a simple Professor class:
by definition, a Professor is a Human (Professor extends Human)
when creating a Professor, you must specify the "name" (which is then passed to the Human constructor)
once you have a Professor, you can call getName() (which is defined on the Human class)
class Professor extends Human {
public Professor(String name) {
super(name);
}
}
Here's a simple Student class:
Student is a little different - in addition to a name, it also has a "semester"
when creating a Student, the constructor requires a name and semester, and the Student class itself keeps track of "semester" – so it's fine to have semester defined on Student, and name defined on Human.
you can call getName() (defined on Human)
you can call getSemester() (defined on Student)
class Student extends Human {
private final int semester;
public Student(String name, int semester) {
super(name);
this.semester = semester;
}
public int getSemester() {
return semester;
}
}
I am fairly new to Inheritance, and I'm not sure if I am doing it right but I seem to be on the right track. The program runs fine except the output I am getting isn't right. I think the problem is to do with my constructors.
public class Person {
protected static String name;
protected static int birthYear;
public Person(String name, int birthYear) {
}
public String name (String n) {
n = name;
return n;
}
public int birthYear (int bY) {
bY = birthYear;
return bY;
}
#Override
public String toString() {
return String.format(name + birthYear);
}
}
public class Student extends Person {
protected String major;
public Student(String name, int birthYear, String major) {
super(name, birthYear);
major = "";
}
public String major(String maj) {
maj = major;
return maj;
}
public String toString() {
super.toString();
return super.toString() + major;
}
}
public class Instructor extends Person {
protected static int salary;
public Instructor(String name, int birthYear, int salary) {
super(name, birthYear);
salary = 0;
}
public int salary(int sal) {
sal = salary;
return sal;
}
public String toString() {
super.toString();
return super.toString() + salary;
}
}
public class PersonTester {
public static void main(String[] args) {
Person p = new Person("Perry", 1959);
Student s = new Student("Sylvia", 1979, "Computer Science");
Instructor e = new Instructor("Edgar", 1969, 65000);
System.out.println(p);
System.out.println("Expected: Person[name=Perry,birthYear=1959]");
System.out.println(s);
System.out.println("Expected:" +
"Student[super=Person[name=Sylvia,birthYear=1979],major=Computer]");
System.out.println(e);
System.out.println("Expected:" + "Instructor[super=Person[name=Edgar,birthYear=1969],salary=65000.0]");
}
}
OUTPUT I AM GETTING:
null0
Expected: Person[name=Perry,birthYear=1959]
null0null
Expected: Student[super=Person[name=Sylvia,birthYear=1979],major=Computer Science]
null00
Expected: Instructor[super=Person[name=Edgar,birthYear=1969],salary=65000.0]
Try changing your constructor in Person to:
public Person(String name, int birthYear) {
this.name = name;
this.birthYear = birthYear;
}
Currently, the constructor has an empty body, so when you call super(name, birthYear); in the subclass constructor, nothing actually happens.
Your Student constructor also has an error. You forgot to initialize the major field.
public Student(String name, int birthYear, String major) {
super(name, birthYear);
this.major = major;
}
You have the same problem in the Instructor constructor...
public Instructor(String name, int birthYear, int salary) {
super(name, birthYear);
this.salary = salary;
}
Finally, you need to take away the static keywords before the fields in Person. This is because static ensures, that there will always be one (and only one) instance of those fields per class, as opposed to one per instance, like you want it to be:
protected String name;
protected int birthYear;
Same thing for the salary field in Instructor.
n = name; this causing your problem. It must be name = n;. All your setter function contain this problem, correct them all and tell me result.
I am trying to access a constructor in an abstract class that is two levels higher.
public abstract class Person{
protected String name;
public Person(String name){
if name.length() <= 12)
this.name = name;
else
this.name = name.substring(0,12);
}
public final String returnName(){
return name;
}
}
public class employee extends person{
public employee(string firstname, string gender){
super(firstname);
this.gender =gender;
}
}
public class dependent extends employee{
public dependent(string firstname, string gender, string relation){
super(firstname);
super(gender);
this.relation = relation;
}
How do I invoke the constructor of the abstract class from the dependent class (two levels below)?
Ok, first of all, here's how you pass two parameters to your Employee constructor:
super(firstname, gender);
Second, there's no need to call the Person constructor from the Dependent one. This will happen automatically when Dependent calls the Employee constructor, because the Employee constructor then calls the Person one.
What you are looking for is something like the below, please note Classes start with Upper-case and camel case for method parameter staring with a lower-case letter. String must be upper-case String name , please also be aware you have introduced a protected member scope on name
See the following post on the implications In Java, difference between default, public, protected, and private
public abstract class Person
{
protected String name;
public Person(String name)
{
if (name.length() <= 12)
{
this.name = name;
}
else
{
this.name = name.substring(0, 12);
}
}
public final String returnName()
{
return name;
}
}
public class Employee extends Person
{
private String gender = null;
public Employee(String firstName, String gender)
{
super(firstName);
this.gender = gender;
}
}
public class Dependent extends Employee
{
private String relation = null;
public Dependent(String firstName, String gender, String relation)
{
super(firstName, gender);
this.relation = relation;
}
}
You will need to add methods to access the relation and gender if you need the access to these
try this:
public class employee extends Person{
public employee(String firstname, String gender){
super(firstname);
this.gender =gender;
}
}
public class dependent extends employee{
public dependent(String firstname, String gender, String relation){
super(firstname,gender);
this.relation = relation;
}
I'm writing a simple program in which I have a super class Person inherited by the sub-classes Customer and Employee (they inherit the variables ID, name and surname).
public class Person {
int id;
String name;
String surname;
public Person() {}
public Person(int i, String n, String s) {
id = i;
name = n;
surname = s;
}
}
public class Employee extends Person implements Serializable {
String username;
String password;
String date;
int hpw;
int recordSold;
float hourPay;
public Employee() {}
public Employee(String u, String n, String s, String p, int i, int h, String d, int rSold, float hPay) {
username = u;
super.name = n;
super.surname = s;
password = p;
super.id = i;
hpw = h;
date = d;
recordSold = rSold;
hourPay = hPay;
}
}
However the problem is here: when I try to get the variables ID, name and surname through my main class, they fail to return (0,null,null). Why is this? I have get-Methods in my sub-classes which should return the super variables, but they are not. Thanks for your time and patience.
public String getName() {
return super.name;
}
UPDATE:
ok so I sorted out the super(id,name,surname) in the Employee class constructor. I also removed all the getters and setters in the employee class since those are inherited from the Person superclass (correct me if I'm wrong?..)
Person superclass:
public class Person {
private int id;
private String name;
private String surname;
public Person () {
}
public Person(int i, String n, String s) {
this.id = i;
this.name = n;
this.surname = s;
}
public void setID(int i) {
this.id = i;
}
public void setName(String n) {
this.name = n;
}
public void setSurname(String s) {
this.surname = s;
}
public int getID() {
return id;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
}
Employee subclass:
import java.io.*;
public class Employee extends Person implements Serializable {
protected String username;
protected String password;
protected String date;
protected int hpw;
protected int recordSold;
protected float hourPay;
public Employee() {
super();
}
public Employee(int i, String u, String n, String s, String p, int h, String d, int r, float hP) {
super(i,n,s);
username = u;
password = p;
date = d;
hpw = h;
recordSold = r;
hourPay = hP;
}
public void setUser(String u) {
username = u;
}
public void setPassword(String p) {
password = p;
}
public void setHWeek (int h) {
hpw = h;
}
public void setDate (String d) {
date = d;
}
public void setRSold (int r) {
recordSold = r;
}
public void setHPay (float p) {
hourPay = p;
}
public String getUser() {
return username;
}
public String getPassword() {
return password;
}
public int getHWeek() {
return hpw;
}
public String getDate() {
return date;
}
public int getRSold() {
return recordSold;
}
public float getHPay() {
return hourPay;
}
however, when I run the main program the ID, name and surname variables are still null, they are not being returned by the superclass. Am I missing something please? Thanks
Inheritance only works for methods NOT for variables. It is also bad practice to implement methods in subclasses that access super class variables directly. You'd better implement access methods in your superclass. Due to inheritance, those methods will be available in the sub-classes ass well.
Another thing is the visibility of you instance varibles. You are using the default visibility which is "package-wide". So if your sub-classes are not in the same package, they can't access those variables. If you use "private" or "protected" visibility you are much safer accessing the variables.
Another point is that you are initializing the objects not correctly. Calling the sub-class constructor has to call the super-class constructor as well because your Employee object relies on the functionality that your Person object provides. A more scientific description of this principle exists:
Barbara Liskov - Liskov substitution principle
public class Person {
private int id;
private String name;
private String surname;
public Person() {}
public Person(int i, String n, String s) {
id = i;
name = n;
surname = s;
}
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
public int getSurname() {
return this.surname;
}
}
Add access methods for super class instance variables and set visibility to private.
public class Employee extends Person implements Serializable {
private String username;
private String password;
private String date;
private int hpw;
private int recordSold;
private float hourPay;
public Employee() {}
public Employee(String u, String n, String s, String p, int i, int h, String d, int rSold, float hPay) {
super(id, name, surname);
this.username = u;
this.password = p;
this.hpw = h;
this.date = d;
this.recordSold = rSold;
this.hourPay = hPay;
}
}
Call the super class constructor for initialization of the super class.
Your code should look something like this:
public class Person {
private int id;
private String name;
private String surname;
public Person (int id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
public int getId() {
return id;
}
... //similarly for getName() and getSurname()
}
public class Employee extends Person {
private String username;
private String password;
private String date;
private int hpw;
private int recordSold;
private float hourPay;
public Employee (int id, String name, String surname, String username, String password, String date, int hpw, int recordSold, float hourPay) {
super(id, name, surname);
this.username = username;
... //similarly for other parameters.
}
}
The important bit is super(id, name, surname).
EDIT
lionc claims that I did not answer the question, which is true. I did this because the original poster seems to be new to Java and, hence, might be asking the "wrong" question. I should have highlighted this in my original response. Given that my answer is currently marked as the best, I believe that I made the right decision.
You haven't initialized those variables, that's why it is returning default value for those variables. In java following are default values for variables.
int -> 0
String -> null (because String is Object in Java)
You define those attributes in both of your classes so you override them in the subclass. Moreover, your Employee constructor is not the way it should. You should call the adapted super-constructor as your first statement.
public class Person {
protected int id;
protected String name;
protected String surname;
public Person(int id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
}
public class Employee extends Person implements Serializable {
private String username;
private String password;
private String date;
private int hpw;
private int recordSold;
private float hourPay;
public Employee(String username, String name, String surname, String pswd, int id,
int hpw, String date, int rSold, float hPay) {
super(id,name,surname);
this.username = username;
this.password = pswd;
this.hpw = hpw;
this.date = date;
this.recordSold = rSold;
this.hourPay = hPay;
}
}
In your constructors, I consider a best practice to give the same name to your parameters as the name of your attributes to initialize and differenciate them thanks to this. Some people also use the same names except that they add a _ at the beginning of all the members of the class. In any case, don't use such meaningless names as "s", "n" etc when the variables they represent have a special meaning (surname, name). Keep those names for example for local variables without any particular semantic (n would be an integer, s would be a String...).
In your example, you don't need tu use super to access the attributes defined in the super class since you are using package visibility for them (and both seems to be in the same package).
However, this is NOT the proper way to write Java code.
You should define a visibility for your attributes. In most case, it is recommended to use private visibility and to define getter and setter methods to access them:
public class Person {
private int id;
private String name;
private String surname;
public Person() {}
public Person(int id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// And so on...
}
In sub-classes, you just have to call getId() or setId(...) to access the Id attribute. No need to call super.getId(). Since Employee extends Person, it has access to all of its public, protected (and package if they are in the same package) attributes and method.
This means that in your current code, you can simply write name = n instead of super.name = n.
public class Employee extends Person implements Serializable {
private String username;
private String password;
private String date;
private int hpw;
private int recordSold;
private float hourPay;
public Employee() {}
public Employee(String username, String name, String surname, String password, int id, int hpw, String date, int rSold, float hPay) {
super(id, name, surname);
this.username = username;
this.password = password;
this.hpw = hpw;
this.date = date;
this.recordSold = rSold;
this.hourPay = hPay;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// And so on...
}
Now to use these classes, you can write code like:
Employee e = new Employee("user3149152", "Ulrich", "Ser", "passwd", 1234, 0, "2014/08/13", 0, 0);
System.out.println("Employee " + e.getName() + ' ' + e.getSurname() + " has for id " + e.getId() + '.');
For reference, this code works even with your current code.
It prints:
Employee Ulrich Ser has for id 1234.
I have a method which creates a new object Student and adds it to an array list studentRegister:
public void addStudent(String name, String age)
{
studentRegister.add(new Student(name, age));
}
it calls the Student class constructor here:
public Student(String name, String age)
{
this.name = name;
this.age = age;
}
This works but it is bad for maintainability as i have to change any additional parameters in both the Student class and the addStudent method. How do I input the parameters at the addStudent stage without having them harcoded in the addStudent method?
just do this:
public void addStudent(Student s)
{
studentRegister.add(s);
}
And in constructer/ other methods you can call the above method as below:
public Student(String name, String age)
{
this.name = name;
this.age = age;
addStudent(this); //here is the call to the above method
}
You should pass a student object - Instead of the two values.
public void addStudent(Student student)
{
studentRegister.add(student);
}
Using
public void addStudent(final Student student) {
studentRegister.add(student);
}
is the better aproach.
Maybe you're looking for a simpler way to build the object. e.g. using chain setters:
public class Student {
private String name;
private String age;
private String address;
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}
public String getAge() {
return age;
}
public Student setAge(String age) {
this.age = age;
return this;
}
public String getAddress() {
return address;
}
public Student setAddress(String address) {
this.address = address;
return this;
}
}
So, would then:
Student student = new Student().setName("User")
.setAge("30")
.setAddress("New York");
Another way for build the object with normal setters:
Student student = new Student(){{
setName("User");
setAge("30");
setAddress("30");
}};