Hi, as I was told that my code doesn’t scale well at all, I thought perhaps I’d try to get a better understanding of interfaces/abstract classes and classes and the relationship between them.
I don’t want at this stage work on a big separate project as I've already got plenty to work on, rather do small exercises to help me understand the concept.
So, I was thinking, maybe I can create a small program that output the characteristics of either an employee or a client of an organisation, perhaps loop through them, just to better understand relationships between classes.
So, while I’m open to suggestions of course, here is the plan:
-create an abstract class Person, that has at least a name and a surname, perhaps a method returning a string
-create a class Employee that implements Person and that perhaps contains its own method (not sure which one at the moment, maybe something that prints "I'm a en employee")
-create a class Client that implements Person and that perhaps contains its own method (not sure which one at the moment, maybe something that prints "I'm a client")
How does that sound as a start?

Edited 3 Months Ago by Violet_82

OK, it's a start. Be careful with your terminology. If Person is a class then the others have to extend it. If it's an interface the others implement it.
Chosing between an abstract superclass and an interface is often one of those difficult decisions. Chose a class and you prevent all its subclasses from extending anything else. Chose an interface and you can't define instance variables. (There are other retrictions in what an interface can do, but Java 8 changed that significantly, eg default methods, so be certain anything you read relates to Java 8 not any earlier version.)

Edited 3 Months Ago by JamesCherrill

Right, so here is the code, it's just a simple application creating one Employee and one Client object and print out the result.
Obviously if there is anything you think isn't right, let me know and I will amend.
What do you reckon would be a good thing to do with this code to, say, enhance it? Perhaps get a list of Clients and/or Employees?

Person.java

public abstract class Person
{
    private String name;
    private String surname;
    private int age;
    abstract void printStatus();
    public Person(String name, String surname, int age){
        this.name = name;
        this.surname = surname;
        this.age = age;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setSurname(String surname){
        this.surname = surname;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public String getSurname(){
        return surname;
    }
    public int getAge(){
        return age;
    }
    @Override
    public String toString(){
        return String.format("name: %s, surname: %s, age: %d ", getName(), getSurname(), getAge());
    }
}

Employee.java

public class Employee extends Person
{
    public Employee(String name, String surname, int age){
        super(name,surname,age);
    }

    @Override
    void printStatus()
    {
        System.out.println("I'm an Employee " + super.toString());

    }
}

Client.java

public class Client extends Person
{
    public Client(String name, String surname, int age){
        super(name,surname,age);
    }
    @Override
    void printStatus()
    {
        System.out.println("I'm a client: " + super.toString());

    }
}

TestPerson.java

public class TestPerson
{

    public static void main(String[] args)
    {
        //create objects of type person
        Employee employee1 = new Employee("John","Smith",39);
        employee1.printStatus();
        Client client1 = new Client("Jack", "Milt", 40);
        client1.printStatus();
    }

}

A few observations:
-In terms of constructors, each concrete class has its own constructor which calls the parent constructor to initialize the instance variable. Is it good practice to provide an empty constructor too inside Person? I mean, really it doesn't serve any purpose because when I create an object I'm passing 3 parameters: a problem might occur if you create an object and pass no parameters, then the application won't compile, so having an empty constructor is just what, error prevention?

If a class needs some variables initialised then an empty constructor is just asking for trouble. Obviously if you don't have one then you can't call new with no parameters, but that makes sense too. (Unless you are writing a factory class.)

Normally every subclass would override toString, your printStatus is a bit redundant. (or just define it once in the superclass as System.out.println(this); )

Maybe add some extra info in the subclasses - eg Employeee::salary and deal with those in the constructor appropriately?

Thanks for the feedback. To be fair the printStatus method is there only because I wanted to have to implement an abstract method, and I couldn't think of anything else :-). I usually go back to my classes after a long time and I thought it'd be nice to have an abstract method there, if I will have another method later on - and I'm sure I will - I can make that one abstract and get rid of printStatus altogether, but yes I see your point, I could call toString directly without going through printStatus.
I will add a few more properties to the class and then post back

Right, changes made. The abstract class now has a new member, salary and the old printStatus is not abstract anymore but I've implemented it in there. I've also added setters and getters for the salary

public abstract class Person
{
    private String name;
    private String surname;
    private int age;
    private double salary;
    //abstract void printStatus();
    public void printStatus(){
        System.out.println(this);
    }
    public Person(String name, String surname, int age, double salary){
        this.name = name;
        this.surname = surname;
        this.age = age;
        this.salary = salary;
    }
    @Override
public String toString(){
    return String.format("\nName: %s,\nsurname: %s,\nage: %d,\nsalary: %.2f\n ", getName(), getSurname(), getAge(), getSalary());
}   
    ...

The two extended classes are now smaller

public class Employee extends Person
{
    public Employee(String name, String surname, int age, double salary){
        super(name,surname,age,salary);
        System.out.print("I'm an Employee. ");
    }
}

and

public class Client extends Person
{
    public Client(String name, String surname, int age, double salary){
        super(name,surname,age,salary);
        System.out.print("I'm an Client. ");
    }
    }

The test class is the same.
Say I wanted to have quite a few instances of EMployees and Client, what would be the best way forward? Don't know I'm just trying to think about what it could be added to it for me to practice a little bit more with inheritance, polymorphism and relationships among classes in general

I think salary belongs in Employee - only employees have a salary. This is typical of how a subclass can extend a superclass to add more info that's just relevant to the subclass.

Override toString in all your classes. You can use the superclass's version to avoid repetition, eg

return "Employee " + super.toString() + " salary = " + salary;

When you have multiple instances it's common to have a Collection to hold them, usually in a class that represents whatever ties those instances together. eg

class SportsClub {
    ...
   List<Person> members = new ArrayList<>();

   public void addNewMember(Person p) ...

   etc

or

 class Company {
         ...
       List<Employee> employees = new ArrayList<>();

       public void addNewEmployee(Employee e) ...

       etc

Edited 3 Months Ago by JamesCherrill

Yes good points actually. So I've removed the salary from the parent class and added it to the employees only. Before getting to the multiple instances though, I thought it'd be nice to use composition, so perhaps I can use another class like Identification, which has an id number and a status, like permanent or contractor. This would only apply to Employees of course. After that I can implement the collection bit.
So, let's see how I can go about this. I'll create the class Identification - for the lack of a better name - and then presumably the Employee's constructor will have to take care of the initialization details, for instance, initializing the ID and the status.
In the meanwhile, here is the current amended code in its entirety:
Person.java

public abstract class Person
{
    private String name;
    private String surname;
    private int age;    
    public void printStatus(){
        System.out.println(this);
    }
    public Person(String name, String surname, int age){
        this.name = name;
        this.surname = surname;
        this.age = age;
        //this.salary = salary;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setSurname(String surname){
        this.surname = surname;
    }
    public void setAge(int age){
        this.age = age;
    }

    public String getName(){
        return name;
    }
    public String getSurname(){
        return surname;
    }
    public int getAge(){
        return age;
    }
    @Override
    public String toString(){
        return String.format("\nName: %s,\nsurname: %s,\nage: %d ", getName(), getSurname(), getAge());
    }
}

Person.java

public class Employee extends Person
{
    private double salary;
    public Employee(String name, String surname, int age, double salary){
        super(name,surname,age);
        //System.out.print("I'm an Employee. ");
        this.salary = salary;
    }
    public double getSalary(){
        return salary;
    }
    public void setSalary(double salary){
        this.salary = salary;
    }
    @Override
    public String toString(){
        return "\nEmployee " + super.toString() + " \nsalary = " + getSalary();
    }

}

Client.java

public class Client extends Person
{
    public Client(String name, String surname, int age){
        super(name,surname,age);
        //System.out.print("I'm an Client. ");
    }
    @Override
    public String toString(){
        return "\nClient " + super.toString();
    }

}

TestPerson.java

public class TestPerson
{

    public static void main(String[] args)
    {
        //create objects of type person
        Employee employee1 = new Employee("John","Smith", 39, 1250.50);
        employee1.printStatus();
        Client client1 = new Client("Jack", "Milt", 40);
        client1.printStatus();
    }

}

I get where you are coming from, but that extra info just looks like ordinary attributes of an employee to me, and not a good reason for another class. How about an Address class (street, postcode etc)

Edited 2 Months Ago by JamesCherrill

eh eh, too late, already implemented it : -)! I see what you mean though, every employee actually has those attributes...Might leave it there for the time being but with the intention of removing it and incorporating the members inside the Employee class.
Another thing I've done was to correctly implement toString and remove that printStatus() method because, as you correctly pointed out, it was useless. Now I print objects directly from the test class like so:
System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
I'll implement the project class as it sounds like a good idea and a better demonstration of the has-a relationship. So what should new class have? I'm thinking a project name of course, perhaps a method that shows whether that project has been assigned, so probably a boolean method that returns true or false somehow, how about that?

You may have noticed that I has second thoughts and changed that suggestion to Address instead - it's easier to see what the attributes are. Projects are more independent, and may have multiple Employees, so it's not strictly speaking composition,.

That's OK. So one thing, if I create an Address class right, considering that both Client and Employee will use it, where would it be the best place to implement it? I was thinking that I could create an object of type Address inside Client and EMployee but that seems like a bit of a repetition. The thing is, I don't believe I can create an object in the abstract class Person though, so perhaps creating two Address objects, one for the Employee and one for the Client class is right

You create an Address class in the same package, alongside the other classes. You can then use that in any or all of the other classes. Because most people do have an address you could create a variable for that in the abstract Person class, along with any methods that may rleate to the Person's address. That way all the other classes will inherit all that.

// example of composition

class Engine {
...
}

class GearBox{
...
}

class Car {
// every Car has an Engine and a Gearbox...
  Engine e;
  Gearbox g;

  // constructor
  Car() {...
      e = new Engine(...
      g = new Gearbox(...

Sure yeah, will do that, but if I then want to print the address I can't do it from the abstract class because that class calls the constructor of Address, so presumably I have to implement ToString in Address and call it from there instead...I believe
EDIT: no, that's a lie, I can access the getters of Address from the abstract class because I have a reference to address, so that's fine!

Edited 2 Months Ago by Violet_82

Address, like pretty much every other class, needs a toString. That's a public method, so you can call it from anywhere. Going back to the car...

class car {
...

@Overide
public String toString() {
   // return details of this car, delegating
   // details of engine and gearbox to
   // those class's toString methods
   return "A car " +
      " with " + e + " and  " + g;
}

will return something like "A car with 6 cyclinder turbo engine and four on the floor"

OK, so would it be wrong to have the address info referenced from the Person class instead? The reason why I'm asking is because in Person I have a new Address object anyway, so I can print the address information directly from the Person class, here is the relevant code

public abstract class Person
{
    private String name;
    private String surname;
    private int age;
    private Address address;    
    public Person(String name, String surname, int age, int streetNumber, String streetName, String postcode){
        this.name = name;
        this.surname = surname;
        this.age = age;
        address = new Address(streetNumber,streetName,postcode);
    }
    ...
    @Override
    public String toString(){
        return String.format("\nName: %s,\nsurname: %s,\nage: %d, \nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getName(), getSurname(), getAge(),address.getStreetNumber(),address.getStreetName(),address.getPostcode());
    }

or is it more correct to print the address info from the address class as you said in your previous reply?

Edited 2 Months Ago by Violet_82

If every Person has an address then Person is the right place for it.

The toString should delegate formatting an Address to the Address class as in my reply. A Person should be able to use an Address without having to know about its internals.
(Think about what happens when you add String city to the Address class - would you remember to update Person's toString?)

There's a similar argument for the constructor - ie it should take an Address as a parameter. Taking the individual fields and creating an Address should not be a responsibilty of the Person class.

OK, makes sense for the toString, although my Address class isn't extending anything, it's just using composition. I've added a toString method in my Address class like so:

@Override
    public String toString(){
        return String.format("\nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getStreetNumber(), getStreetName(), getPostcode());     
    }

so that it knows how to print itself and then, in the Person class since I create an object of type Address somewhere I can reference that in the Person's toString like so:

@Override
    public String toString(){       
        return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
    }

Which means that I don't need to reference each field of Address like before as you suggested.
About the constructor, I understand what you mean, but how do I pass an object containing all the Address details? What I mean is,currently inside TestPerson I create objects of type Employee and Client passing the relevant address details to it

...
Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", 34, "Flinch Street","KT25AG");
Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", 44, "London Road", "SW17FG");
Client client1 = new Client("Jack", "Milt", 40, 356, "Dean Street", "SW94TG");
...

These in turn calls the relevant constructors (Employee and Client) which then call the parent constructor (Person) to initialize the members of the Address class. I appreciate that Person shouldn't be the one creating the Address, but if I don't create it there I will have to create it where, TestPerson?
cheers

Edited 2 Months Ago by Violet_82

I think Person is the right place, but it should take into account more possibilities:
For instance, an appartment: can a housenumber only be numerical (1, 2, 3, ...) or do you take living units into account (1, 2a, 2b, 2c, 3/101, 3/102, ... )

What when you encounter a homeless person? you should be able to have null, ...

Sir Charles Antony Richard Hoare, inventor of null once said

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Using null to mean "not applicable" or "none" is an NPE waiting to blow up your program when you least expect it. Some better solutions are:
a valid Address instance accessible via a public static declarationAddress.NONE that behaves appropriately.
A HomelessPerson sub-class, if there are other consquences of not having an address
or
(this is the "correct" Java 8 answer): use an Optional<Address> since that's exactly what you mean and it's eactly what Optional was designed for
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

As for where to create an address..,
There is a debate to be had here, but my opinion is to prefer

Address addr = new Address (blah blah blah);
Employee e = new Employee(blah, blah, addr);

because when you change houseNumber to String you will have to change the call to the constructor(s) anyway, but like this you don't have to change Person. It's fundamental encapsulation - Person should have no knowledge of Address's internals unless it's absolutely essential to Person's functioning.

ps:

my Address class isn't extending anything

Your Address class extends Object, even though you didn't need to declare that explicitly. You needed to override the inherited toString() because the one in Object is useless to you.

Edited 2 Months Ago by JamesCherrill

OK, so not sure I've done it correctly. Here is the new logic followed by the code.
TestPerson.java - assuming that everybody has an address as said before - creates the Employees, Clients and Address objects, here the class in full:

public class TestPerson
{

    public static void main(String[] args)
    {       
        Address address = new Address(34, "Flinch Street","KT25AG");
        Address address1 = new Address(44, "London Road", "SW17FG");
        Address address2 = new Address(356, "Dean Street", "SW94TG");
        Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", address);
        Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", address1);
        Client client1 = new Client("Jack", "Milt", 40, address2);
        System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
    }
}

The employees and clients object contains an address as well so no need to include that inside the print statement.
Then in Employee I have the Address object passed to it which in turns gets passed to Person so it can get printed:

public Employee(String name, String surname, int age, double salary, int id, String role, Address address){     
        super(name,surname,age, address);
        ...
       @Override
public String toString(){
    return "\nEmployee " + super.toString() + " \nsalary = " + getSalary() + "\nID is " + identification.getID() + " \nRole is " + identification.getRole();
} 
        ...

And Person

...
    public Person(String name, String surname, int age, Address address){
    ...
    @Override
    public String toString(){           
        return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
    }

Trouble is it's returning a NullPointerException, but surely the Address object should be initialized OK by now

Exception in thread "main" java.lang.NullPointerException
    at Person.toString(Person.java:41)
    at Employee.toString(Employee.java:23)
    at java.util.Formatter$FormatSpecifier.printString(Formatter.java:2838)
    at java.util.Formatter$FormatSpecifier.print(Formatter.java:2718)
    at java.util.Formatter.format(Formatter.java:2488)
    at java.io.PrintStream.format(PrintStream.java:970)
    at java.io.PrintStream.printf(PrintStream.java:871)
    at TestPerson.main(TestPerson.java:20)

Edited 2 Months Ago by Violet_82

There's not enough code there to be sure, but at a guess in Person's constructor you are failing to save the address parameter correctly into the instance variable (or maybe one of the other variables?) ?

Edited 2 Months Ago by JamesCherrill

Yes, spot on, I wasn't assigning address to anything, no wonder it didn't work : -)! Below is the full code so far. I think that I'd like to add my employees and clients objects in a List, as we discussed earlier, and perhaps sort them in alphabetical order, somthing like that, unless there is anything else that I need to change before doing so:
Person.java

public abstract class Person
{
    private String name;
    private String surname;
    private int age;
    private Address address;
    public Person(String name, String surname, int age, Address address){   
        this.name = name;
        this.surname = surname;
        this.age = age;
        this.address = address;

    }
    public void setName(String name){
        this.name = name;
    }
    public void setSurname(String surname){
        this.surname = surname;
    }
    public void setAge(int age){
        this.age = age;
    }

    public String getName(){
        return name;
    }
    public String getSurname(){
        return surname;
    }
    public int getAge(){
        return age;
    }
    @Override
    public String toString(){       
        return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
    }
}

Employee.java

public class Employee extends Person
{
    private double salary;
    private Identification identification;
    //private Address address;
    public Employee(String name, String surname, int age, double salary, int id, String role, Address address){
    //public Employee(String name, String surname, int age, double salary, int id, String role, int streetNumber, String streetName, String postcode){
        super(name,surname,age, address);
        //System.out.print("I'm an Employee. ");
        this.salary = salary;
        identification = new Identification(id, role);
        //address = new Address(streetNumber, streetName, postcode);
    }
    public double getSalary(){
        return salary;
    }
    public void setSalary(double salary){
        this.salary = salary;
    }
    @Override
    public String toString(){
        return "\nEmployee " + super.toString() + " \nsalary = " + getSalary() + "\nID is " + identification.getID() + " \nRole is " + identification.getRole();
    }
}

Client.java

public class Client extends Person
{
    public Client(String name, String surname, int age, Address address){
        super(name,surname,age,address);
        //System.out.print("I'm an Client. ");
    }
    @Override
    public String toString(){
        return "\nClient " + super.toString();
    }
}

Address.java

public class Address
{
    private int streetNumber;
    private String streetName;
    private String postcode;

    public Address(int streetNumber, String streetName, String postcode){
        this.streetNumber = streetNumber;
        this.streetName = streetName;
        this.postcode = postcode;
    }   
    public int getStreetNumber(){
        return streetNumber;
    }
    public String getStreetName(){
        return streetName;
    }
    public String getPostcode(){
        return postcode;
    }
    @Override
    public String toString(){
        return String.format("\nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getStreetNumber(), getStreetName(), getPostcode());

    }
}

Identification.java

public class Identification
{
    private int ID;
    private String role;//permanent or contractor
    public Identification(int ID, String role){
        this.ID = ID;
        this.role = role;
    }
    public int getID(){
        return ID;
    }
    public String getRole(){
        return role;
    }
}

TestPerson.java

public class TestPerson
{

    public static void main(String[] args)
    {
        //create objects of type person     
        Address address = new Address(34, "Flinch Street","KT25AG");
        Address address1 = new Address(44, "London Road", "SW17FG");
        Address address2 = new Address(356, "Dean Street", "SW94TG");
        Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", address);
        Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", address1);
        Client client1 = new Client("Jack", "Milt", 40, address2);
        System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
    }

}

Yes, that's enough for you to be able to move on to storing Collections of people.
Before coding anything, think about where you might find a collection of people in real life (eg a club or company) and if you were the secretary or personnal officer what would you need to be able to do with that collection. Write that down, the imagine it as a method signatures, then start coding.

The article starter has earned a lot of community kudos, and such articles offer a bounty for quality replies.