Inheritance, some examples for SCJP study

Inheritance is a practice of one thing extending another. So you can more specific subclasses of a class. Inheritance is available in most programming languages, and in Java it is implied using the extends keyword.

For example, you may have several objects like Car, Boat, Truck, these may all have common behaviour and state between them. You could duplicate the shared members between them, but that can lead to a maintenance nightmare for some poor man in the future. The best option would be too look at inheritance, and try to move common, generic code to a superclass, such as Vehicle.

Then, your subclasses can all extend Vehicle, and automatically inherit the shared members, so you only have them coded once. Inheritance can do down as many levels as you like, and both classes and interfaces can use inheritance (a class can extend another class, and an interface can extend another interface, but never implement it).

In Java, you can only extend a single class at a time, which prevents multiple inheritance that you would otherwise get in another language.

Consider the below sample that I have on my SCJP samples Github project, which portrays a simple inheritance structure:

package com.jameselsey.demo.scjp.oo_concepts;

/**
 * Author:  JElsey
 * Date:    16/08/2012
 *
 * Sample of inheritance, showing a few classes that extend from each other, there is a little bit of polymorphism in here
 * too, can you spot it?
 */
public class Inheritance
{
    public static void main(String[] args)
    {
        Grandfather grandfather = new Grandfather();
        Daddy daddy = new Daddy();
        Son son = new Son();

        // The Grandfather is at the top of the inheritance tree (extends from java.lang.Object), so he only has wisdomLevel
        System.out.println("The grandfather has a wisdom level of " + grandfather.wisdomLevel);

        // The Daddy is strong, and also inherits his wisdom
        System.out.println("The daddy is " + daddy.strengthLevel + " and also gets the grandfathers wisdom: " + daddy.wisdomLevel);

        // The boy is lucky, hes very energetic, but also inherits strength from his dad, and wisdom from his grandfather
        System.out.println("The son is " + son.energyLevel + " and " + son.strengthLevel + " and " + son.wisdomLevel);

        // Inheritance also works on methods too, since they're all part of the same family, we can safely put the
        // family name in the grandfather, both the son and daddy will inherit that method
        System.out.println("Grandfathers surname : " + grandfather.getFamilyName());
        System.out.println("Daddys surname : " + daddy.getFamilyName());
        System.out.println("Sons surname : " + son.getFamilyName());
    }
}

class Grandfather
{
    String wisdomLevel = "Wise";

    public String getFamilyName()
    {
        return "Jones";
    }
}

class Daddy extends Grandfather
{
    String strengthLevel = "Strong";
}

class Son extends Daddy
{
    String energyLevel = "Energetic";
}

Encapsulation; shielding state via the use of accessors

Encapsulation is a fantastic OOP concept of being able to shield class state via the use of accessor methods.

Imagine that you have a class, perhaps called Person. This class will have some form of state, such as name, age, date of birth. These would most likely be stored against reference variables. If you left these publicly available, then anyone who develops against your class can freely access them and assign them to whatever they feel necessary.

Later on, you may like to change the way some of this state is represented, for example you may wish to start storing ages as Strings instead of Integers, if you do that, then anyone who is dependant on your class will now be screaming up and down as you would have broken their code that interfaces with yours, as you’ve changed the data type and it will cause compilation issues for them.

Encapsulation helps protect against these scenarios, whereby you would make the state private so other people can’t access it directly, but at the same time you would provide public methods so that developers can still use your class, and those public methods would access the private state.

This means that if you change your internal representation, you’d just need to update the implementation of the accessor method to retrieve the new data type, convert it to what the old API was expecting, and return it. Developers using your code won’t be interested in how its stored internally provided they can get their data in and out, if you change something behind the scenes, they won’t care (maybe they would, but this is just an example).

By forcing developers to access state via accessor methods, you can also enforce other restrictions like validation, without encapsulation the developers could put any value into name, and there is nothing you could do about it. However, using accessor methods, you could check the value beforehand and see that it meets your requirements, if it doesn’t, throw an exception.

Encapsulation is generally perceived to be good practice, and you should try to use it where possible. Some languages, such as Groovy even implement this out of the box for you, unless you state otherwise.

Please consider this example from my SCJP samples Github project, which displays the basics of encapsulation:

package com.jameselsey.demo.scjp.oo_concepts;

/**
 * Author:  JElsey
 * Date:     16/08/2012
 *
 * Demonstrating an understanding of the Encapsulation OOP term, which basically means to shield instance behaviour
 * behind publicly accessible accessors, thus giving more control of the internal representation of the values
 * and provides a single point of access.
 */
public class Encapsulation
{
    public static void main(String[] args)
    {
       // Create a class that doesn't use encapsulation

        UnencapsulatedMess mess = new UnencapsulatedMess();
        mess.personName = "James";
        mess.personAge = 26;
        System.out.println(mess);

        // now create a similar class, but one which implements encapsulation, notice the use of the accessor methods
        // rather than direct variable access
        EncapsulatedDream dream = new EncapsulatedDream();
        dream.setPersonName("James");
        dream.setPersonAge(26);
        System.out.println(dream);


        // Now, lets create another mess, but this time we want to have some control over what values the variable takes
        UnencapsulatedMess anotherMess = new UnencapsulatedMess();
        String someNameThatWouldComeFromAnotherClass = "Daniel";
        int anAgeThatWouldComeFromAnotherClass = 12;
        if (anAgeThatWouldComeFromAnotherClass < 16 || anAgeThatWouldComeFromAnotherClass > 60)
        {
            // handle an error
        }
        else
        {
            anotherMess.personAge = anAgeThatWouldComeFromAnotherClass;
            anotherMess.personName = someNameThatWouldComeFromAnotherClass;
        }
        // Woah! A lot of waffle to validate a value before we place it, and whereever you refer to this unecapsulated
        // class, you'd have to do the same thing, and how easy that would be to get wrong or be inconsistent!

        // With the encapsulated class, you could do all your validation in a central place, and be sure it is ALWAYS invoked,
        // This may not be the BEST place to validate, but its just an example of the benefits of encapsulation.
    }
}


class UnencapsulatedMess
{
    public String personName;
    // no access modifier specified, so takes default
    int personAge;

    @Override
    public String toString()
    {
        return "UnencapsulatedMess{" +
                "personName='" + personName + '\'' +
                ", personAge=" + personAge +
                '}';
    }
}

class EncapsulatedDream
{
    // make the state private, so only accessible within the class itself.
    private String personName;
    private int personAge;

    // Accessors
    public String getPersonName()
    {
        return personName;
    }

    public void setPersonName(String personName)
    {
        this.personName = personName;
    }

    public int getPersonAge()
    {
        return personAge;
    }

    /**
     * Another useful utlitly method that encapsulation aids with, returning the person
     * age as a user friendly, readable String.
     * @return String the persons age as a String
     */
    public String getPersonAgeAsString()
    {
        return personAge + " years old";
    }

    /**
     * This is an accessor method to set the persons age, this method also performs some validation
     * on the value being set
     * @param personAge int the persons age, in years
     */
    public void setPersonAge(int personAge)
    {
        if (personAge < 16 || personAge > 60)
        {
            // Do something here! Throw an exception or log something
        }
        else
        {
            this.personAge = personAge;
        }
    }

    @Override
    public String toString()
    {
        return "EncapsulatedDream{" +
                "personName='" + personName + '\'' +
                ", personAge=" + personAge +
                '}';
    }
}