Skip to content

Instantly share code, notes, and snippets.

@itszechs
Last active February 22, 2025 03:50
Show Gist options
  • Select an option

  • Save itszechs/99f8476be59f3e037241cfd9467d70d6 to your computer and use it in GitHub Desktop.

Select an option

Save itszechs/99f8476be59f3e037241cfd9467d70d6 to your computer and use it in GitHub Desktop.
Notes on OOP

Object Oriented Programming in JAVA

In object-oriented programming, you have classes and objects, unlike functional programming. There are 4 main pillars in OOP Paradigm.

Pillars of Object-Oriented Programming

  1. Abstraction
  2. Encapsulation
  3. Polymorphism
  4. Inheritance

1. Abstraction

Abstraction refers to the idea of identifying common properties and methods between similar objects and implementing them as abstract classes or interfaces.

Abstraction using Abstract classes

By using abstract classes we can define, abstract methods as well as concrete methods and fields which are common throughout its inheritance. It is possible to override and add additional functionality to concrete methods or even completely replacing its functionality but not calling super().

Example of Abstraction using Abstract classes

abstract class Shape {
  protected final String name;

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

  public abstract int area();

  public abstract int perimeter();
}

class Rectangle extends Shape {
  private final int height;
  private final int width;

  public Rectangle(int height, int width) {
      super("Rectangle");
      this.height = height;
      this.width = width;
  }

  @Override
  public int area() {
      return width * height;
  }

  @Override
  public int perimeter() {
      return 2 * (width + height);
  }
}

class Circle extends Shape {
  private final int radius;

  public Circle(int radius) {
      super("Circle");
      this.radius = radius;
  }

  @Override
  public int area() {
      return (int) (Math.PI * radius * radius);
  }

  @Override
  public int perimeter() {
      return (int) (2 * Math.PI * radius);
  }
}

class AbstractExample {
  public static void main(String[] args) {
      Shape rectangle = new Rectangle(5, 4);
      Shape circle = new Circle(3);

      System.out.println("Area of " + rectangle.name + " is " + rectangle.area());
      System.out.println("Perimeter of " + rectangle.name + " is " + rectangle.perimeter());


      System.out.println("Area of " + circle.name + " is " + circle.area());
      System.out.println("Perimeter of " + circle.name + " is " + circle.perimeter());
  }
}

Abstraction using Interfaces

By using interfaces we can define a skeleton structure which inheriting class must implement.

Example of Abstraction using Interfaces

interface Stack {
  void pop();

  void push(int data);

  int top();

  void print();

  int size();

  boolean isEmpty();

}

class StackUsingArray implements Stack {

  private final int[] stack;
  private final int capacity;
  private int size = 0;

  public StackUsingArray(int capacity) {
      stack = new int[capacity];
      this.capacity = capacity;
  }

  @Override
  public void pop() {
      if (size <= 0) {
          throw new IllegalStateException("Stack is empty.");
      }
      size--;
  }

  @Override
  public void push(int data) {
      if (size >= capacity) {
          throw new IllegalStateException("Stack is full.");
      }
      stack[size] = data;
      size++;
  }

  @Override
  public int top() {
      if (size < 0) {
          throw new IllegalStateException("Stack is empty");
      }
      return stack[size - 1];
  }

  @Override
  public void print() {
      for (int i = size - 1; i >= 0; i--) {
          System.out.print(stack[i] + " ");
      }
      System.out.println();
  }

  @Override
  public int size() {
      return size;
  }

  @Override
  public boolean isEmpty() {
      return size == 0;
  }

}

class StackUsingLinkedList implements Stack {

  private DoublyNode head = null;
  private DoublyNode last = null;
  private int size = 0;

  @Override
  public void pop() {
      last = last.prev;
      size--;
  }

  @Override
  public void push(int data) {
      DoublyNode newNode = new DoublyNode(data);
      if (head == null) {
          head = newNode;
          last = head;
      } else {
          newNode.prev = last;
          last.next = newNode;
          last = newNode;
      }
      size++;
  }

  @Override
  public int top() {
      return last.data;
  }

  @Override
  public void print() {
      DoublyNode temp = last;
      while (temp != null) {
          System.out.print(temp.data + " ");
          temp = temp.prev;
      }
      System.out.println();
  }

  @Override
  public int size() {
      return size;
  }

  @Override
  public boolean isEmpty() {
      return size == 0;
  }

}

class StackExample {

  public static void main(String[] args) {
      testStack(new StackUsingArray(50));
      testStack(new StackUsingLinkedList());
  }

  private static void testStack(Stack myStack) {
      myStack.push(1);
      myStack.push(2);
      myStack.push(3);

      System.out.print("Stack after push operations: ");
      myStack.print();

      System.out.println("Top element: " + myStack.top());

      myStack.pop();

      System.out.print("Stack after pop operation: ");
      myStack.print();

      System.out.println("Top element: " + myStack.top());
      System.out.println("Size of the stack: " + myStack.size());
      System.out.println("Is the stack empty? " + myStack.isEmpty());
  }

}

2. Encapsulation

Suppose we have multiple fields for an object, let's take Employee as an example. An Employee can have id, name, salary etc. So with Data Encapsulation we can define a class which represents a Employee.

Example of Data Encapsulation

class Employee {
  private final int id;
  private final String name;
  private final long salary;

  public Employee(int id, String name, long salary) {
      this.id = id;
      this.name = name;
      this.salary = salary;
  }

  public int getId() {
      return id;
  }

  public String getName() {
      return name;
  }

  public long getSalary() {
      return salary;
  }

  @Override
  public String toString() {
      return "My name is " + getName() + ", my employee id is " + getId() + " and I make " + getSalary() + " USD";
  }
}


class EncapsulationExample {
  public static void main(String[] args) {
      Employee johnDoe = new Employee(1, "John Doe", 1000);
      Employee janeDoe = new Employee(2, "Jane Doe", 1200);
      System.out.println(johnDoe.toString());
      System.out.println(janeDoe.toString());
  }
}

3. Polymorphism

As we understand from the name, something which can morph and change its form, we have abstract methods, and interfaces in java which are good example of polymorphism as they make use of method overriding and method overloading in some cases.

Example of Polymorphism

interface Addition {
  int add(int a, int b);
  double add(double a, double b);
}

class AdditionImpl implements Addition {

  // Note the data-types, that's method overloading
  
  @Override
  public int add(int a, int b) {
      return a + b;
  }


  @Override
  public double add(double a, double b) {
      return a + b;
  }

}

class PolymorphismExample {
  public static void main(String[] args) {
      Addition addition = new AdditionImpl();
      System.out.println(addition.add(1, 2));
      System.out.println(addition.add(1.0, 2.0));
  }
}

4. Inheritance

It refers to one class inheriting another class. There are 5 types of inheritance:

  • Single level inheritance
  • Multi level inheritance
  • Hierarchical inheritance
  • Multiple inheriance (not supported in Java)
  • Hybrid inheritance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment