Classes and Objects

So far we only worked with primitives like int, boolean, float, double etc.. But Java provides more than primitives. Primitives are great for mathematicians to solve equations and such. However the real power in Java is the ability to create objects.

Java is an Object Oriented Programming (OOP) language. What it means by that is, you can write a Java program by making use of OOP principles which includes; ability to create objects, ability to use inheritance, polymorphism, method overriding, method overloading etc., on objects. You will understand all of these terms in detail in this lesson. So far we were more focused on understanding logic flow, simple algorithms and basic constructs of programming, which are mostly common across all types of programming languages.

Why should we use OOP concepts while writing software programs?

To solve mathematical equations, OOP concepts are not that important. However, when you build a sophisticated software platform like Amazon, using OOP concepts while writing programs will make things easy for the entire organization to do their business. OOP helps in replicating the real world entities in software very easily. A website like Amazon is in principle trying to simulate the real world brick and mortar stores in software and OOP helps do exactly that.

Before you write your software, first it is important to understand the real world stores. So what entities constitute the business of a brick and mortar store?

Well, here are some types of entities we can think of:

  • Customers who visit the stores
  • Customers pick up a Shopping Cart to get their items
  • Customers pick up their Items to buy and place them in their shopping cart
  • Customers get their items checked out at the counter.

When you replicate this in software, it is always a good idea to abstract your programs exactly like the way it is in the real world, so that, everyone in the organization are able to communicate in business terms, weather it is software they are talking about, or the real business. You see, after all software programs are written to help the business and not the other way around. Using OOP concepts help us write a software program which replicates the real world!

Every customer who walks into the shop is represented as an Object and every Shopping Cart that he pulls is also considered another type of Object. Similarly the items the customer picks up to add to his shopping cart are yet another type of Object.

Almost all mega software projects which incorporates OOP principles, mimic the real world business or enterprise. OOP was introduced for the very reason to help mimic the real world.

A 'class' is a template

In Java, a class is also a template or a blueprint from which objects are created. A class is indeed the fundamental construct to create objects. You define a unique class for every unique type of Object you want to create.

You can compare a class to a chocolate mould in a chocolate factory. While the mould (class) is used to create chocolates (objects), it is the chocolate that we are more interested in than the mould.

image alt text

In a similar way in OOP, we heavily use objects that are created out of the class than the class itself. It is important to understand that a class can be defined for not only writing a simple program with a main method but can also be used to create Objects.

In this lesson you will learn the art of defining a class with the goal of creating Objects out of it. Then, you will learn how to create Objects using the class.

Before you understand more on objects, let us understand the basic computer and how it stores data. Every computer has two types of memory;

  • Hard disk - Data stored on Hard disk is permanent and remains on computer between computer restarts. E.g., your java class file that you saved on your hard disk will be available even if you shutdown and restart your computer.
  • Random Access Memory (a.k.a RAM) - The data stored in RAM is temporary. The data stored on RAM will be marked for deletion as soon as the program which created the data finishes its execution. RAM is wiped clean when the computer shuts down. RAM is the play arena where your java programs run. Your java program creates objects and variables in RAM. Which also means that, these objects will vanish as soon you shut down your computer or after the program ends. And the next time you start your computer again and rerun your program, all the objects and variables will be recreated.

OOP use case

Supposing you were tasked to create an online book store in Java, how would you start?

To begin with you list out all the possible class definitions that you may have to create to mimic the real world book store. Let us list out some basic types that you will need:

  • We need a class for representing Books
  • We need a class for representing Customers
  • We need a class for representing Shopping Carts
  • We need a class to calculate billing for a customer
  • We need a class to hold the shipping information of the customer

Although the list goes on, let us first take the Book class to understand the basic principles. A book store will keep many books and we need a way to represent every single book in the software. For this you first create a class called Book defined below:

class Book {

}

Even though the above class is a valid class, it is empty and does not depict anything useful. To make it useful, you can add the following constructs into your class;

  1. Add instance variables - these help hold the state of an object
  2. Add methods - these constructs help define the behavior of an object
  3. Add constructors - these construct are used to create new objects

Let us add these three constructs to make our Book class more useful.

Instance Variables

Instance variables are added when ever you want to hold a unique state of every object. To mimic the real world book, at the very least, need the following instance variables in Book class

  • Book Title
  • Book Author
  • Book Price

Here is the class with these variables added

class Book {
    String title;
    String author;
    double price;
}

Now we can create an object of class Book and hold some important state values of a book. Since books are in a bookstore, we will create another class called BookStore which will have the main method.

public class BookStore {

    public static void main(String[] args) {
        Book book1 = new Book();
        book1.title = "Harry Potter and the Sorcerer's Stone";
        book1.author = "J.K. Rowling";
        book1.price = 29;

        System.out.println(book1.title);
    }
}

class Book {
    String title;
    String author;
    double price;
}

Output: Harry Potter and the Sorcerer's Stone

In the above example, you initialized the three variables (title, author and price) using the dot(.) operator on the object. E.g.;

book1.price = 29;

and you accessed the initialized value for printing, using the dot operator again in the system.out.println statement: book.title

Notice the new keyword used to create a Book object. Once the object is created using the new keyword, it is assigned to a variable called book1 which is of type Book. So, you instantiated an object of class Book and you initialized its instance variables which identifies the specific book of interest. Now if you want to create a second book, then you would instantiate the Book class again to get another book. You can again set its instance variables which identify the second book.

public class BookStore {

    public static void main(String[] args) {
        Book book1 = new Book();
        book1.title = "Harry Potter and the Sorcerer's Stone";
        book1.author = "J.K. Rowling";
        book1.price = 29;

        System.out.println(book1.title);

        Book book2 = new Book();
        book2.title = "Harry Potter and the Prisoner of Azkaban";
        book2.author = "J.K. Rowling";
        book2.price = 31;

        System.out.println(book2.title);
    }
}

class Book {
    String title;
    String author;
    double price;
}

Output: Harry Potter and the Sorcerer's Stone
Harry Potter and the Prisoner of Azkaban

What is happening in RAM?

As and when you create objects, a new space is allocated to keep the objects and its instance values. The more objects you create, the more of your RAM gets filled up with the created objects. Each object uniquely identifies one and only one book. Now you have successfully implemented OOP concept in creating books!

The figure below shows the depiction of how objects may occupy the RAM area.

image alt text

Methods

So far we have been working with the main method. This method is absolutely required to run a program from command line. However when it comes to OOP, you define a method to add functionality for every action that an object can take. Remember method names should be a verb form as they are represent some kind of action being carried out by the object. So what actions can objects take?

Let us take a look at some examples. Let us consider a ShoppingCart class.

  1. You may have to add one method called addItem(Item item) which takes an Item object to be added to the shopping cart object.
  2. Once an item is added to the shopping cart, you should also give an ability to remove the item in case the customer changed his mind. So for that you may add a method called removeItem(Item item) which removes item that is given to the method.
  3. Once all the items are added, you may want to add a method to find the total of all the items which are being purchased. For that you may add a method with the name like getTotal()

Along with the above methods it is also a good idea to provide methods for setting the values for all the instance variables. You also add a private modifier for the instance variables. This technique is called Encapsulation and that is not covered in this eBook.

Using this keyword

Instance variables and methods which do not have a static keyword can be referenced using this keyword. The idea behind that is this refers to the specific instance object of the class for which the values are being set. If it is a method, then an action is being performed on the specific instance object by using the this keyword. Let us see an example:


class Book {
    String title;
    String author;
    double price;

    void setTitle(String title){
        this.title = title;
    }
}

In the above example, you see a setTitle method which takes a String argument and the value that is passed in the argument is used to set the instance variable 'title' by referencing it using the this keyword.

If you do not use this keyword in this example then it would be referencing the local variable 'title' which is nothing but the method argument that is passed in.

Constructors

In the above examples, you were able to construct an instance using the new keyword using the default constructor. Default constructor is defined in the top level class called Object. Every class in Java inherits from the top level Object class. That is reason you did not have to define the default constructor in your class. However when you add any other constructor, you have to override the default constructor of the Object class.

Constructor is defined very similar to a method except the name of the constructor should be the same as the class name and constructors do not have a return type.

Here is an example:

class Book {

     private String title;
     private String author;
     private double price;

     public void setTitle(String title) {
         this.title = title;
     }
     public void setAuthor(String author) {
         this.author = author;
     }
     public void setPrice(double price) {
         this.price = price;
     }

     public String getTitle() {
         return this.title;
     }
     public String getAuthor() {
         return this.author;
     }
     public double getPrice() {
         return this.price;
     }

     // default no argument constructor. 
     // Need not define if there are no other constructors defined.
     public Book() {

     }

     // constructor which takes one argument title. 
     // The instance value of title is initialized in this constructor
     public Book(String title) {
         this.title = title;
     }

     // constructor which takes two argument; title and author. 
     // The instance value of title and author is initialized in this constructor
     public Book(String title, String author) {
         this.title = title;
         this.author = author;
     }


     public static void main(String[] args) {
         Book book1  = new Book();
         book1.setTitle("Harry Potter and the Sorcerer's Stone");
         System.out.println(book1.getTitle());

         Book book2 = new Book("Harry Potter and the Prisoner of Azkaban");
         System.out.println(book2.getTitle());
     }
 }

Output:

Harry Potter and the Sorcerer's Stone Harry Potter and the Prisoner of Azkaban

In the above example, you see three constructors defined for Book class

  • No argument constructor. If there are no other constructors defined, then there is no need to define this constructor as every class inherits the no argument constructor from the top level class Object.
  • Constructor which takes one argument title. The argument value 'title' initializes the instance variable 'title' when this constructor is invoked.
  • Constructor which takes two arguments; title and author - Here also argument values received through the constructor, initializes the instance variables of the object when this constructor is invoked.

Exercise: Can you add one more constructor which takes three arguments 'title', 'author' and 'price' ?

Notice the output of the program. You did not have to set the title using the setter method when you used the second constructor. You add additional constructors when you have all the variable values right at the time the object is instantiated. That way you initialize the values through the constructors and save yourself from adding many setter statements in the code.

Recap - Parts of a Class and its OOP relation

  • A class contains 0 to more constructors using which an object can be created. If there are no constructors defined then a default, no argument constructor, is used from the top level Object Class.
  • A class contains 0 to more instance variables which are used to hold the state of the object that is created from this class.
  • A Class contains 0 to more methods which represent action that the object created from this class can take.
  • Main method is not the method that is meant for object usage. Main method is only used for starting your program from the command line.

Inheritance (IS-A relationship)

Inheritance is another important feature of OOP. It is a mechanism by which one class (a.k.a. child class) is allowed to keep the variables and methods declared in another class (a.k.a parent class). The child class is declared by extending the parent class using the extend keyword. Inheritance allows us to create hierarchy among classes to help organize classes in a meaningful way.

In our examples, let us assume that our BookStore sells two types of books: E-Books and Hard covered books. Both types of books need the instance variables that are declared above. However E-Books need an extra instance variable to hold the URL of the book and the Hard covered books do not need this variable. But the hard covered books need an instance variable to hold the value of the quantity of books in stock, which the E-Books do not need. Instead of creating two classes and repeating the common variables, you can keep the above class Book and create two child classes called EBook and HardCoveredBook by extending the parent class Book, thereby inheriting the common variables. Lets do that as below:


class Book {

     private String title;
     private String author;
     private double price;

     public void setTitle(String title) {
         this.title = title;
     }
     public void setAuthor(String author) {
         this.author = author;
     }
     public void setPrice(double price) {
         this.price = price;
     }

     public String getTitle() {
         return this.title;
     }
     public String getAuthor() {
         return this.author;
     }
     public double getPrice() {
         return this.price;
     }
 }

class EBook extends Book {

    private String url;

    public void setUrl(String url) {
        this.url = url;
    }
}

class HardCoveredBook extends Book {

    private int qtyInStock;

    public void setQtyInStock(int qty) {
        this.qtyInStock= qty;
    }
}

public class BookStore {

     public static void main(String[] args) {
         EBook book1 = new EBook();
         book1.setTitle("Harry Potter and the Sorcerer's Stone");
         book1.setAuthor("J.K. Rowling");
         book1.setPrice(29);
         book1.setUrl("http://ebooks.zzz/harrypotter");

         System.out.println(book1.getTitle());

         HardCoveredBook book2 = new HardCoveredBook();
         book2.setTitle("Harry Potter and the Prisoner of Azkaban");
         book2.setAuthor("J.K. Rowling");
         book2.setPrice(31);
         book2.setQtyInStock(20);

         System.out.println(book2.getTitle());
     }
 }

Notice that you are able to set title, author and price variables on EBook and HardCoveredBook even though they are not declared in EBook or HardCoveredBook classes. You are able to do that because you extended the Book class which has these variables. Try removing the extend keyword and see if you can still access the variables.

Points to note

  • You use extends keyword to make a child of another class.

  • Parent class is also called Super class

  • Child class is also called Sub class

  • All the parents methods, variables and constructors are visible to the child except for the ones declared private

  • When ever you instantiate a child, a corresponding parent object is also created along with the child.

 

IS-A relationship

  • An instance of a child class is not only of the type of the child class but also of the parent. In the above example, instance book1 is of type EBook and also of type Book. In OOP we say book1 IS-A EBook and book1 IS-A Book also.
What is method overriding?

If the child class has the same method signature as the parent, then it is called overriding. The child class (a.k.a subclass) will override the parent method as it needs to give a different implementation for the same method. Let us take an example:


class Animal {

    public void speak() {

        System.out.println("Every animal speaks in its own way");
    }
}

class Cat extends Animal {

    public void speak() {
        System.out.println("meow meow");
    }

}


public class AnimalKingdom {

    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.speak();

        Cat myCat = new Cat();
        myCat.speak();

    }
}

In the above example, the Cat class overrides the speak method of the Animal as it is giving specific way in which a Cat speaks.

Run the above code and see the output. What do you see? Now remove the speak method in Cat and then run the same program. What do you see now? Do you understand why the output is different?

bulb It is called overriding only if the method signatures match between the parent and the child class. If the parameter list is different between the parent and child class then it is called overloading which is covered in the next module.

results matching ""

    No results matching ""