DEV Community

Michael Otu
Michael Otu

Posted on • Updated on

A Simple Introduction To Java - Object Oriented Programming - Part 4 (Abstraction)


In this session, we will discuss:

Abstraction

There four OOP concepts: Encapsulation, Inheritance, Polymorphism and Abstraction. We have discussed the Encapsulation, Inheritance and Polymorphism. Now we will consider the last of it, Abstraction.

So what is Abstraction?

We have been using the Shape class for a while and we thank the "coiner" of the Shape class.

This is one of the early implementations of the Shape class:

public class Shape {
    double length;
    double breadth;

    public Shape(double length, double breadth) {
        this.length = length;
        this.breadth = breadth;
    }

    public double area() {
        return this.length * this.breadth;
    }

    public double perimeter() {
        return 2 * (this.length + this.breadth);
    }

    public void print() {
        System.out.println("The shape has a length and a breadth of, "
                + this.length + " and " + this.breadth + ".");
        System.out.println("Shape has an Area of " + this.area()
                + " squared units.");
        System.out.println("Shape has a Perimeter of " + this.perimeter()
                + " units.");
    }
}
Enter fullscreen mode Exit fullscreen mode

According to this class, we assume that every Shape has length and breadth as properties, and every Shape has a method for computing the area and perimeter of the said Shape. However pleasant and useful it was, there were some flaws in the implementation and the idea as a whole.

The Shape class was based on Plane figures.

Let's consider some Plane figures like the Circle, Square, Rectangle, Triangle, Parallelogram and the others. They have some similarities and earlier on in Inheritance, I mentioned that we give the similarities to the superclass and the subclasses inherit the superclass and implement their differences. Yeah, it made sense at the time, however, this was based on the idea that they'd all the similar implementations for some methods or something like that. That idea is not bad in itself but it is flawed. Every Shape should have a method of computing the area and perimeter, but these implementations are unique to each Shape.

So what has Abstraction got to do with this?

This is of the idea that for there to be a Shape of any sort, this Shape must have a method for computing the area and the perimeter. For this idea, we will create a class that strictly enforces this concept.

An abstract is kind of an idea, and for there to exist this idea, whatever model that assumes this idea must conform to the idea. However, each model has its way of expressing this idea. (This sounds like Polymorphism)

I remember reading the Art and Science of C and when we got to Chapter 8. Designing Interfaces: A Random Number Library, the book discussed header files in the light of an API or interfaces. For this, let me share some C code snippets.

/*
gcdlcm.c

A program that computes the gcd and lcm of two integers, x and y
*/

#include <stdio.h>

/* function definition of gcd */
int gcd(int x, int y);

/* function definition of lcm */
int lcm(int x, int y);

// entry point
int main()
{
    int x = 12, y = 18;

    printf("The gcd of %d and %d is %d\n", x, y, gcd(x, y));

    printf("The lcm of %d and %d is %d\n", x, y, lcm(x, y));
}

/* function implementation for gcd */
int gcd(int x, int y)
{
    if (y == 0)
    {
        return x;
    }

    return gcd(y, x % y);
}

/* function implementation for lcm */
int lcm(int x, int y)
{
    return (x * y) / gcd(x, y);
}

Enter fullscreen mode Exit fullscreen mode

We can compile this snippet by running, gcc gcdlcm.c then ./a.out (Linux here). The output was,

The gcd of 12 and 18 is 6
The lcm of 12 and 18 is 36

Enter fullscreen mode Exit fullscreen mode

In my own words

If a user, just like above, wants to use the gcd and lcm functions, they don't have to necessarily know about how the gcd and lcm functions are implemented. For someone who wants to use any of the functions, just the header file or the function definitions in the header file is enough to give the user enough information about how to use the function. The user of the functions must trust me, the designer of the function that if they call the gcd function and pass in two integer arguments the gcd function will return an integer.

In Java, we can achieve Abstraction using abstract classes and interface.

Now answering the question earlier, What is Abstraction?

When we expose or provide the user of our class with the necessary information on how to use our class without giving out any information on how the class was implemented is known as Abstraction. We concentrate on the idea rather than the implementation.

Some features of an Abstraction class

  • An Abstraction class is defined using the abstract keyword
  • We can not create an object or instance of an Abstract class
  • To create an instance of the Abstract class, we have to create a subclass of the Abstract class and then create an instance of the subclass
  • An Abstract class may have Abstract methods (An Abstract method is that with a method definition without implementation - just like in the gcd and lcm)
  • An Abstract class may have properties and methods just like any other class
  • The Abstract methods must be implemented by the subclass

Some examples

Let's provide a different implementation of the Shape class. We will make the Shape class an abstract class and make the area and perimeter methods abstract as well.

public abstract class Shape {
    private String name;
    private double side;

    public Shape(String name, double side) {
        this.name = name;
        this.side = side;
    }

    public double getSide() {
        return this.side;
    }

    // abstract methods
    public abstract double area();

    public abstract double perimeter();

    // default methods
    public void print() {
        System.out.print("This " + this.name + " has an Area of "
                + this.area() + " squared units");

        System.out.println(" and a Perimeter of "
                + this.perimeter() + " units.");
    }
}

Enter fullscreen mode Exit fullscreen mode

In the above class, name and side are private. We have a single constructor that takes the name and side as an argument. There is a getter method for side since the computation of the area and perimeter will be based on the side. There are two abstract methods area and perimeter. Whatever class that extends the Shape class must (has to) implement these methods. We have a default implementation for the print method. This means we can override the print method.

Know that as far as a single method in a class is abstract, the whole class must become an abstract class.

Since we can not create an instance of the Shape class, let's create another Shape, Square that will inherit the Shape class. (This is like Inheritance and Polymorphism bundled together)

public class Square extends Shape {

    public Square(String name, double side) {
        super(name, side);
    }

    /* area = side * side or side squared */
    public double area() {
        return Math.pow(this.getSide(), 2);
    }

    /* perimeter = side + side + side + side or 4 * side */
    public double perimeter() {
        return 4 * this.getSide();
    }
}

Enter fullscreen mode Exit fullscreen mode

In the Square class, we passed the name of the current Shape and the dimension of the side as an argument to the Square constructor which is then passed to the constructor of the superclass. We implemented the area and perimeter methods. We will call the print method when we create an instance of the Square class.

public class Main {
    public static void main(String[] args) {
        Square square = new Square("Square", 2.5);
        square.print();
    }
}
// Output:
// This Square has an Area of 6.25 squared units and a Perimeter of 10.0 units.
Enter fullscreen mode Exit fullscreen mode

Let's add another Shape class, Circle.

public class Circle extends Shape {
    public Circle(String name, double radius) {
        super(name, radius);
    }

    /* area = PI * radius * radius or PI * radius squared */
    public double area() {
        return Math.round(Math.PI * Math.pow(this.getSide(), 2));
    }

    /*
     * A Circle's perimeter is the same as its circumference
     * perimeter = 2 * PI * radius
     */
    public double perimeter() {
        return Math.round(2 * Math.PI * this.getSide());
    }
}

Enter fullscreen mode Exit fullscreen mode

From the implementation of the area and perimeter methods, it is obvious that these are different from that of the Square class. I added the round method from the Math class. Now we can create an instance of the Circle class and call its print method.

public class Main {
    public static void main(String[] args) {
        Square square = new Square("Square", 2.5);
        square.print();

        Circle circle = new Circle("Circle", 2.5);
        circle.print();
    }
}
// output:
// This Square has an Area of 6.25 squared units and a Perimeter of 10.0 units.
// This Circle has an Area of 163.0 squared units and a Perimeter of 45.0 units.

Enter fullscreen mode Exit fullscreen mode

Interface

An Interface is an Abstract class that only has abstract methods to be implemented. To create an Interface, we use the interface keyword. Unlike the Abstract classes, properties in an interface can or must only be public, static or final. So after they are defined, they can't be altered. An interface is not "a class" so we can not instantiate it nor give it a constructor. We can extend an interface using another interface. We can "implement" several interfaces. In java we can not extend several classes to make them an interface when then we implement them.

Since an Interface is an Abstract class, we don't have to add the abstract or class keyword when we want to create an interface. Just like an Abstract class, all the methods in an interface are abstract so we don't have to declare them as abstract. The abstract methods of an Abstract class are meant to be implemented by the class that extends hence they are exposed to the public and by default all methods in an interface are public.

Remember, A subclass can have only one superclass but several interfaces to implement

Let's make an interface for our Shape abstract class.

// IShape.java
public interface IShape {

    public double area();

    public double perimeter();

    public void print();
}

Enter fullscreen mode Exit fullscreen mode

The preceding I indicates an Interface. To implement the abstract methods, we have to create a class that will implement the IShape interface. In place of extends for a class, we use implements for Interface.

// Square.java
public class Square implements IShape {
    private double side;

    public Square(double side) {
        this.side = side;
    }

    public double area() {
        return Math.pow(this.side, 2);
    }

    public double perimeter() {
        return 4 * this.side;
    }

    public void print() {
        System.out.print("This square has an area of " + this.area());
        System.out.println(" and a perimeter of " + this.perimeter());
    }
}


Enter fullscreen mode Exit fullscreen mode

Say we have a Shape that must implement just one of the abstract methods, what do we do? Remember that we have to implement all the abstract methods. In a case like this, it will be enough if we split the IShape to IArea and IPerimeter, and if we want both together we can either implement both or create another interface, IAreaPerimeter.

// IArea.java
public interface IArea {
    public double area();
}

Enter fullscreen mode Exit fullscreen mode
// IPerimeter.java
public interface IPerimeter {
    public double perimeter();
}

Enter fullscreen mode Exit fullscreen mode

Consider GumShape that implements IArea and overloads its abstract method.

public class GumShape implements IArea {
    public double area() {
        return Math.PI * 3;
    }

    public double area(int s) {
        return Math.PI + s;
    }
}

Enter fullscreen mode Exit fullscreen mode

I will update my Main class:

public class Main {
    public static void main(String[] args) {
        // Square square = new Square(2.5);
        // square.print();

        // Circle circle = new Circle("Circle", 7.2);
        // circle.print();

        GumShape gs = new GumShape();
        System.out.println("Gum has an area of " + gs.area(5));
    }
}

Enter fullscreen mode Exit fullscreen mode

I didn't call the area() method but the area(int s). If the area method with no parameter was not implemented, there'd be an error.

public class GumShape implements IArea, IPerimeter {
    public double area() {
        return Math.PI * 3;
    }

    public double area(int s) {
        return Math.PI + s;
    }

    public double perimeter() {
        return Math.PI + 4;
    }

Enter fullscreen mode Exit fullscreen mode

Say we have a Shape that must implement just one of the abstract methods, what do we do? Remember that we have to implement all the abstract methods. In a case like this, it will be enough if we split the IShape to IArea and IPerimeter, and if we want both together we can either implement both or create another interface, IAreaPerimeter.

// IArea.java
public interface IArea {
    public double area();
}

Enter fullscreen mode Exit fullscreen mode
// IPerimeter.java
public interface IPerimeter {
    public double perimeter();
}

Enter fullscreen mode Exit fullscreen mode

Consider GumShape that implements IArea and overloads its abstract method.

public class GumShape implements IArea {
    public double area() {
        return Math.PI * 3;
    }

    public double area(int s) {
        return Math.PI + s;
    }
}

Enter fullscreen mode Exit fullscreen mode

I will update my Main class:

public class Main {
    public static void main(String[] args) {
        // Square square = new Square(2.5);
        // square.print();

        // Circle circle = new Circle("Circle", 7.2);
        // circle.print();

        GumShape gs = new GumShape();
        System.out.println("Gum has an area of " + gs.area(5));
    }
}

Enter fullscreen mode Exit fullscreen mode

I didn't call the area() method but the area(int s). If the area method with no parameter was not implemented, there'd be an error.

public class GumShape implements IArea, IPerimeter {
    public double area() {
        return Math.PI * 3;
    }

    public double area(int s) {
        return Math.PI + s;
    }

    // public double perimeter() {
    // return Math.PI + 4;
    // }
}

Enter fullscreen mode Exit fullscreen mode

I commented out the implementation of the perimeter method as such we'd get an error. Again,

public class GumShape implements IAreaPerimeter {
    public double area() {
        return Math.PI * 3;
    }

    // public double area(int s) {
    // return Math.PI + s;
    // }

    public double perimeter() {
        return Math.PI + 4;
    }
}

Enter fullscreen mode Exit fullscreen mode

This is the same as public class GumShape implements IArea, IPerimeter {.

I commented out the implementation of the perimeter method as such we'd get an error. Again,

public class GumShape implements IAreaPerimeter {
    public double area() {
        return Math.PI * 3;
    }

    // public double area(int s) {
    // return Math.PI + s;
    // }

    public double perimeter() {
        return Math.PI + 4;
    }
}

Enter fullscreen mode Exit fullscreen mode

This is the same as public class GumShape implements IArea, IPerimeter {.

Conclusion

Abstraction, also known as Data Abstraction, is an OOP concept where we expose the necessary information about how to use a class (and its methods) but not how the class (and its methods) are implemented. Abstraction is implementation hiding as Inheritance is to data hiding.

  • Abstract classes are declared abstract and have at least one abstract method
  • Abstract classes can not be instantiated
  • Abstract classes can be subclassed
  • Abstract classes may have properties and methods
  • Abstract methods must be implemented by the subclass

Another to implement an Abstraction is to use an interface.

  • Interfaces are Abstract classes
  • Interfaces have only abstract methods
  • Interfaces are declared using the interface keyword
  • Interfaces must have public, static or final properties
  • Interfaces can be implemented by a class using the implements keyword

If you now stepping into the OOP world, Maxi Contieri has a lot of articles that talk about OOP and other concepts in general. I do read his articles and I believe they will be very helpful to you as well. Consider following Maxi Contieri.

Project

  • Using the Shape abstract class implement the classes, Rectangle and Triangle, which extends the Shape and implements its abstract methods.
  • Using the IShape interface implement the classes, Circle, Rectangle and Triangle, which implements the IShape and implements its abstract methods.

Source

  • Sololearn
  • DS Malik

Top

Top comments (0)