Object-Oriented Concepts and Constructs

From Sinfronteras
Revision as of 14:01, 10 April 2018 by Adelo Vieira (talk | contribs) (Nested classes)
Jump to: navigation, search

SOLID Principles

  • S ingle Responsibility Principle
  • O pen Closed Principle
  • L iskov's Substitution Principle
  • I nterface Segregation Principle
  • D ependency Inversion Principle

Single Responsibility Principle

  • One class should have one and only one responsibility:
In other words, you should write, change and maintain a class for only one purpose.
  • If it is a model class then it should strictly represent only one actor/ entity. This will give you the flexibility to make changes in future without worrying about the impacts of changes for another entity.
  • Similarly, If you are writing service/manager class then it should contain only that part of the method calls and nothing else.

Open Closed Principle

  • Software components should be open for extension, but closed for modification:
What does it mean?
  • It means that your classes should be designed in such a way that whenever fellow developers want to change the flow of control in specific conditions in an application, all they need to do is extend your class and override some methods and that's it.
  • If other developers are not able to design the desired behaviour due to the constraints of your class, then you should reconsider changing your class. This is not to say that anybody can change the whole logic of your class, but they should be able to override the options provided by the software in an un-harmful way.
  • For example, if you take a look into any good framework like struts or spring, you will see that you can not change their core logic and request processing, BUT you can modify the desired application flow just by extending some classes and plugging them into configuration files.

Liskov's Substitution Principle

  • Derived types must be completely substitutable for their base types
This means that the classes a fellow developer created by extending your class should be able to fit in the application without failure. It should not break the application or should not throw fatal exceptions.

Interface Segregation Principle

This is applicable to interfaces as the single responsibility principle holds to classes. It states:

  • Clients should not be forced to implement unnecessary methods which they will not use:
    • Take an example. A developer creates an interface -Reportable- and adds two methods generateExcel() and generatedPdf(). Now client 'A' wants to use this interface but they intend to use reports only in PDF format and not in excel. Will they achieve this functionality easily?
      • NO. They will have to implement two methods,
      • So what is the solution? Create two interfaces by breaking the existing one.
They could be PdfReportable and ExcelReportable. This will give the flexibility to the user to use only required functionality.

Dependency Inversion Principle

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

This basically means that we should use an interface to provide a layer of abstraction between high and low level classes.

Class vs Object vs Instance

https://alfredjava.wordpress.com/2008/07/08/class-vs-object-vs-instance/

In OO Programming, we often hear of terms like "Class", "Object" and "Instance"; but what actually is a Class / Object / Instance?

In short, An object is a software bundle of related state and behavior. A class is a blueprint or prototype from which objects are created. An instance is a single and unique unit of a class.

Example, we have a blueprint (class) represents student (object) with fields like name, age, course (class member). And we have 2 students here, Foo and Bob. So, Foo and Bob is 2 different instances of the class (Student class) that represent object (Student people).

Let me go into details...

Object

Real world objects shares 2 main characteristics, state and behavior. Human have state (name, age) and behavior (running, sleeping). Car have state (current speed, current gear) and state (applying brake, changing gear). Software objects are conceptually similar to real-world objects: they too consist of state and related behavior. An object stores its state in fields and exposes its behavior through methods.

Class

Class is a “template” / “blueprint” that is used to create objects. Basically, a class will consists of field, static field, method, static method and constructor. Field is used to hold the state of the class (eg: name of Student object). Method is used to represent the behavior of the class (eg: how a Student object going to stand-up). Constructor is used to create a new Instance of the Class.

Instance

An instance is a unique copy of a Class that representing an Object. When a new instance of a class is created, the JVM will allocate a room of memory for that class instance.

Otro punto de vista

Luego de leer distintos fuentes para tratar de entender los conceptos de «Class vs Object vs Instance» me ha parecido que los conceptos de «Object and Instance» no están bien diferenciados... es decir no encuentro una definición que verdaderamente me convenza de la diferencia entre estos dos conceptos.

Entonces, encontré otra fuente que plantea lo siguiente: https://www.javatpoint.com/q/3420/difference-between-instance-and-object-?

Object and Instance are the same thing in most object-oriented languages. "Instance of a class" is just how the term "object" is defined.

Every object is an instance of a class. And every instance of a class is an object. You can basically interchange these.

You have 5 apples in your basket. Each of those apples is an object of type Apple, which has some characteristics (i.e. big, round, grows on trees).

In programming terms, you can have a class called Apple, which has variables size:big, shape:round, habitat:grows on trees. To have 5 apples in your basket, you need to instantiate 5 apples. Apple apple1, Apple apple2, Apple apple3 etc....

Alternatively: Objects are the definitions of something, instances are the physical things.

No me queda clara la diferencia entre un Object y una Instance. Tampoco me queda claro si se habla de Instance of a Class or Instance of an Object.

Variables

  • Holder for information
  • Not a fixed value, contents can be changed
  • Every variable has a type

Types of variables (clasificación 1)

Type can be:

  • Primitive
  • Object

Primitive - Simple data type

  • E.g. int, double, char, boolean
  • Built into Java
  • Can only hold one piece of data of a specific type
  • All begin with a lowercase letter
Primitive in memory
  • Actual data is stored within variable
    • E.g. int num = 78;
Primitive in memory1.png
  • When you change a primitive variable, the value inside the variable changes:
  • E.g. int x = 12;
  • num = x;
Primitive in memory2.png

Object - Complex data type

  • Created based on classes. These can be either built into Java (E.g. Scanner, String, ArrayList) or user-defined (e.g. Item)
  • Can hold multiple pieces of data of different types
  • All begin with an uppercase letter
Object in memory
  • Variable contains a reference to the location where the data is stored.
    • E.g. Item item = new Item(“Bread”, 2.50, 1);
Object in memory1.png
  • When you change information inside an object, the value (reference) inside the variable stays the same.
  • E.g. item.setName("apple"); (Es decir, aquí estoy cambiando la data (la información que se encuentra en la dirección indicada por la variable) pero lo que en realidad es el «valor de la variable» es la referencia (la dirección que indica (o que referencia) la data)).
  • When you change an object variable, you change the reference:
Object in memory2.png
More on Objects
  • Objects consist of:
    • Fields (instance variables)
    • Constructors
    • Methods
  • When an Object is declared, it is null. The variable contains no reference.
    • If an Object is null, you cannot use any of its methods.
  • When an Object is initialised, it is created fully.
    • Constructor is called & a copy of all fields and methods are created in memory.

Types of variables (clasificación 2)

Instance variable

Instance variables are declared in a class, but outside a method, constructor or any block.

For each instantiated object of the class, a separate copy of each instance variables is made.

Static variable

An instance variable shared by every instance of a class.

public class Item {
    private String name;
    private double price;
    private int quantity;

    private static ItemCount;
...
}

«itemCount» is a static variable, only one copy ever exists in memory.

Useful for:

  • Specific tasks needing centralized data.
    • E.g. keeping a count of the number of instances of a class created.

Local variable

A variable created within the braces (curly brackets) of a method (any method).

A new copy of the local variable is created every time the corresponding method is run.

Parameter variable

A parameter (Method Parameters) is a variable created within the brackets of a method signature. a new copy is created every time the corresponding method is run.

public void increaseQuantity(int quant) {
    quantity = quantity + quant;
}

«quant» is a parameter variable.

Two types of parameter passing:

  • Pass by value - copy of value is sent to method
    • Used when passing primitive types as parameters
  • Pass by reference - copy of location (arrow) is sent to method
    • Used when passing objects as parameters

Variable Lifetime

  • Lifetime: How long a variable continues to exist before it is destroyed/erased from memory.
    • The lifetime of an instance variable is as long as the object it belongs to exists in memory.
    • Lifetime of a local variable is as long as its "parent" method runs.
    • Lifetime of a parameter variable is as long as its "parent" method runs.
    • Lifetime of a static variable is from the first mention of its "parent" class.

The this Keyword

this is a reference to the current object - the object whose method or constructor is being called.

this is used a lot in constructors.


this avoids confusion where local variables have same name as global variables, for example:

public Person(String name1, Date dob1, String address1) {
    name = name1;
    dob = dob1;
    address = address1;
}

Vs.

public Person(String name, Date dob, String address) {
    this.name = name;
    this.dob = dob;
    this.address = address;
}


The this keyword can also be used to call a constructor of this instance of a class, e.g:

public class Rectangle {
    private int x, y;
    private int width, height;

    public Rectangle() {
        this(0, 0, 0, 0);
    }

    public Rectangle(int width, int height) {
        this(0, 0, width, height);
    }

    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
}


Here is given the 6 usage of java this keyword: (https://www.javatpoint.com/this-keyword)

  • this can be used to refer current class instance variable.
  • this can be used to invoke current class method (implicitly)
  • this() can be used to invoke current class constructor.
  • this can be passed as an argument in the method call.
  • this can be passed as argument in the constructor call.
  • this can be used to return the current class instance from the method.

Modifiers

Two types:

  • public (defaulted to when there’s no modifier stated)
  • private

public

public: all-access pass:

  • Anything can access/change a public variable
  • Anything can run a public method

private

private: Cannot be accessed outside the class in which it's defined:

  • Only methods within that class can access/change a private variable
  • Only methods within that class can run a private method
  • Cannot access private variables in a different instance
  • Can only use the private modifier on variables on outer-most level of a class
    • i.e. cannot make any local variables (variables within methods) private
public class Item {
    private String name;
    private double price;
    private int quantity;
    ...
}

Methods

Method Overloading

Overloading: Multiple methods with the same name but different parameter lists.

In constructors:

Item item1 = new Item();
Item milk = new Item(milk, 0.99, 3);

String x = new String();
String y = new String(Hello);

In methods:

String x = new String("Hola, cómo estás");
x.substring(2);
x.substring(2, 5);
x.indexOf("h");
x.indexOf("h", 1);

Standard Methods

Two methods that should be included in classes (Good practice to include it):

  • toString
  • equals

The toString Methodg

  • Returns data stored in an object as a String:
    • Allows the contents of an object to be displayed
    • Can format the contents whatever way you wish
    • MUST return a String*
    • MUST NOT take a parameter*
public String toString() {
    return name + ", " + day + " " + monthNames[month-1] + " " + year + ", " + address;
}

Example result:

"P. Sherman, 30 May 2003, 42 Wallaby Way, Sydney"

Four fundamental OOP concepts

  • Encapsulation
  • Inheritance
  • Polymorphism
  • Abstraction

Encapsulation

Encapsulation in Java is a mechanism of wrapping the data (variables) and code acting on the data (methods) together as a single unit. In encapsulation the variables of a class will be hidden from other classes, and can be accessed only through the methods of their current class, therefore it is also known as data hiding.

To achieve encapsulation in Java:

  • Declare the variables of a class as private.
  • Provide public setter and getter methods to modify and view the variables values.
TestEncapsulation.java
public class TestEncapsulation{
    
    private String name;
    private String idNum;
    private int age;
   
    public int getAge(){
        return age;
    }
    
    public String getName(){
        return name;
    }
    
    public String getIdNum(){
        return idNum;
    }
    
    public void setAge(int newAge){
        age = newAge;
    }
    
    public void setName(String newName){
        name = newName;
    }
    
    public void setIdNum(String newId){
        idNum = newId;
    }
    
}

The public setXXX() and getXXX() methods are the access points of the instance variables of the TestEncapsulation class. Normally, these methods are referred as getters and setters. Therefore any class that wants to access the variables should access them through these getters and setters.

The variables of the EncapTest class can be accessed as below:

/* File name : RunEncapsulation.java */

public class RunEncapsulation{
    public static void main(String args[]){
        TestEncapsulation encap = new TestEncapsulation();
        encap.setName("James");
        encap.setAge(20);
        encap.setIdNum("12343ms");
        
        System.out.print("Name : " + encap.getName() + " Age : " + encap.getAge());
    }
}

This would produce the following result:

Name : James Age : 20

Benefits of Encapsulation:

  • The fields of a class can be made read-only or write-only.
  • A class can have total control over what is stored in its fields.
  • The users of a class do not know how the class stores its data. A class can change the data type of a field and users of the class do not need to change any of their code.

Inheritance

Inheritance can be defined as the process where one class acquires the properties (methods and fields) of another. With the use of inheritance the information is made manageable in a hierarchical order.

The class which inherits the properties of other is known as subclass (derived class, child class) and the class whose properties are inherited is known as superclass (base class, parent class).


extends keyword

extends is the keyword used to inherit the properties of a class. With use of the extends keyword the subclasses will be able to inherit all the properties of the superclass except for the private properties of the superclass.

Example:

class Super{
    ...
    ...
}


class Sub extends Super{
    ...
    ...
}
Calculation.java
package calculation;

class Calculation {
    int z;
    
    public void addition(int x, int y){
        z=x+y;
        System.out.println("The sum of the given numbers:"+z);
    }
    
    public void substraction(int x,int y){
        z=x-y;
        System.out.println("The difference between the given numbers:"+z);
    }
}
MyCalculation.java
package calculation;

public class MyCalculation extends Calculation{
    
    public void multiplication(int x, int y){
        z = x*y;
        System.out.println("The product of the given numbers is: "+z);
    }
    
    public static void main(String args[]){
        int a=20, b=10;
        MyCalculation demo = new MyCalculation();
        demo.addition(a,b);
        demo.substraction(a, b);
        demo.multiplication(a, b);
    }
}

Using extends keyword, My_Calculation inherits the methods addition() and Subtraction() from Calculation class.

You can instantiate the class as given below as well. But using the superclass reference variable ( cal in this case ) you cannot call the method multiplication(), which belongs to the subclass My_Calculation:

Calculation cal = new My_Calculation();
demo.addition(a, b);
demo.Subtraction(a, b);

A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

The super keyword

The super keyword is similar to this keyword. The following are the scenarios where the super keyword is used.

  • It is used to differentiate the members of superclass from the members of subclass, if they have same names.
  • It is used to invoke the superclass constructor from subclass.
Differentiating the members

If a class is inheriting the properties of another class, and if the members of the superclass have the same names as the sub class, to differentiate these variables we use super keyword as shown below.

super.variable
super.method();


Sample Code

In the given program you have two classes namely Sub_class and Super_class, both have a method named display() with different implementations, and a variable named num with different values.


package btest2_the_super_keyword;

class Super_class{
    int num=20;

    //display method of superclass
    public void display(){
        System.out.println("This is the display method of superclass");
    }
}
package btest2_the_super_keyword;

public class Sub_class extends Super_class {
    int num=10;
    
    //display method of sub class
    public void display(){
        System.out.println("This is the display method of subclass");
    }
    
    public void my_method(){
        //Instantiating subclass
        Sub_class sub=new Sub_class();
        
        //Invoking the display() method of sub class
        sub.display();
        
        //Invoking the display() method of superclass
        super.display();
        
        //printing the value of variable num of subclass
        System.out.println("value of the variable named num in sub class:"+ sub.num);
        
        //printing the value of variable num of superclass
        System.out.println("value of the variable named num in super class:"+ super.num);
        
    }

    public static void main(String args[]){
        Sub_class obj = new Sub_class();
        obj.my_method();
    }
    
}
Invoking Superclass constructor

If a class is inheriting the properties of another class, the subclass automatically acquires the default constructor of the super class. But if you want to call a parameterized constructor of the super class, you need to use the super keyword as shown below.

super(values);
class SuperClass{
    
    int age;
    
    SuperClass(int age){
        this.age=age;
    }
    
    public void getAge(){
        System.out.println("The value of the variable named age in super class is: " +age);
    }
}
public class SubClass extends SuperClass {
    
    SubClass(int age){
        super(age);
    }
    
    public static void main(String argd[]){
        SubClass s = new SubClass(24);
        s.getAge();
    }
    
}

IS-A relationship

IS-A is a way of saying : This object is a type of that object.

public class Animal{

}

public class Mammal extends Animal{

}

public class Reptile extends Animal{

}

public class Dog extends Mammal{
    public static void main(String args[]){
        Animal a = new Animal();
        Mammal m = new Mammal();
        Dog    d = new Dog();

        System.out.println(m instanceof Animal); // true
        System.out.println(d instanceof Mammal); // true
        System.out.println(d instanceof Animal); // true
    }

}

The following are true:

  • Animal is the superclass of Mammal class.
  • Animal is the superclass of Reptile class.
  • Mammal and Reptile are subclasses of Animal class.
  • Dog is the subclass of both Mammal and Animal classes.

Now, if we consider the IS-A relationship, we can say:

  • Mammal IS-A Animal
  • Reptile IS-A Animal
  • Dog IS-A Mammal
  • Therefore : Dog IS-A Animal as well

HAS-A relationship

These relationships are mainly based on the usage. This determines whether a certain class HAS-A certain thing. This relationship helps to reduce duplication of code as well as bugs.

public class Vehicle{} 

public class Speed{} 

public class Van extends Vehicle{
    private Speed sp;
}

This shows that class Van HAS-A Speed. By having a separate class for Speed, we do not have to put the entire code that belongs to speed inside the Van class. We also can reuse the Speed class in multiple applications.

Types of inheritance

Types of inheritance

A very important fact to remember is that Java does not support multiple inheritance. This means that a class cannot extend more than one class. Therefore following is illegal:

public class extends Animal, Mammal{} // Illegal in Java

However, a class can implement one or more interfaces. This has made Java get rid of the impossibility of multiple inheritance.

Polymorphism

Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.

Any Java object that can pass more than one IS-A test is considered to be polymorphic. In Java, all Java objects are polymorphic since any object will pass the IS-A test for their own type and for the class Object.

It is important to know that the only possible way to access an object is through a reference variable. A reference variable can be of only one type. Once declared, the type of a reference variable cannot be changed.

The reference variable can be reassigned to other objects provided that it is not declared final. The type of the reference variable would determine the methods that it can invoke on the object.

Let us look at an example:

public interface Vegetarian{}

public class Animal{}

public class Deer extends Animal implements Vegetarian{}

The Deer class is considered to be polymorphic since this has multiple inheritance. The following are true for the above example:

  • A Deer IS-A Animal
  • A Deer IS-A Vegetarian
  • A Deer IS-A Deer
  • A Deer IS-A Object

When we apply the reference variable facts to a Deer object reference, the following declarations are legal:

Deer d = new Deer();

Animal a = d;

Vegetarian v = d;

Object o = d;

All the reference variables d,a,v,o refer to the same Deer object in the heap.

Virtual Methods

The behaviour of overridden methods in Java allows you to take advantage of polymorphism when designing your classes.

Method overriding, where a child class can override a method in its parent. An overridden method is essentially hidden in the parent class, and is not invoked unless the child class uses the super keyword within the overriding method.

package virtualmethods;

public class Employee {
    
    private String name;
    private String address;
    private int    number; 
    
    public Employee(String name, String address, int number) { 
        System.out.println("Constructing an Employee"); 
        this.name    = name; 
        this.address = address; 
        this.number  = number; 
    } 
    
    public void mailCheck() { 
        System.out.println("Mailing a check to "+ this.name + " "+ this.address);
    }
    
    public String toString() { 
        return name + " " + address + " " + number; 
    } 
    
    public String getName() { 
        return name; 
    } 
    
    public String getAddress() { 
        return address; 
    } 
    
    public void setAddress(String newAddress) {
        address = newAddress; 
    } 
    
    public int getNumber() {
        return number; 
    }
    
}
package virtualmethods;

public class Salary extends Employee {
    
    private double salary;
    
    public Salary(String name, String address, int number, double salary) { 
        super(name, address, number);
        setSalary(salary);
    } 
    
    public void mailCheck() {
        System.out.println("Within mailCheck of Salary class ");
        System.out.println("Mailing check to "+getName()+ " with salary "+salary);
    }
    
    public double getSalary() {
        return salary;
    }
    
    public void setSalary(double newSalary){
        if(newSalary >= 0.0){
            salary = newSalary;
        }
    }
    
    public double computePay(){
        System.out.println("Computing salary pay for " + getName());
        return salary/52;
    }
    
}
package virtualmethods;

public class VirtualDemo {
    public static void main(String [] args){
        
        Salary   s = new Salary("Joe Bloggs", "Dublin 10", 3, 3600.00);
        Employee e = new Salary("John Adams", "Dublin 12", 2, 2400.00);
        
        System.out.println("\nCall mailCheck using Salary reference:");        
        s.mailCheck();
        
        System.out.println("\nCall mailCheck using Employee reference:");
        e.mailCheck();
        
    }
    
}

This would produce the following result:

Constructing an Employee
Constructing an Employee

Call mailCheck using Salary reference:
Within mailCheck of Salary class 
Mailing check to Joe Bloggs with salary 3600.0

Call mailCheck using Employee reference:
Mailing a check to John Adams Dublin 12

Here, we instantiate two Salary objects . one using a Salary reference s, and the other using an Employee reference e. While invoking s.mailCheck() the compiler sees mailCheck() in the Salary class at compile time, and the JVM invokes mailCheck() in the Salary class at run time.

Invoking mailCheck() on e is quite different because e is an Employee reference. When the compiler sees e.mailCheck(), the compiler sees the mailCheck() method in the Employee class.

Here, at compile time, the compiler used mailCheck() in Employee to validate this statement. At run time, however, the JVM invokes mailCheck() in the Salary class.

This behavior is referred to as virtual method invocation, and the methods are referred to as virtual methods. All methods in Java behave in this manner, whereby an overridden method is invoked at run time, no matter what data type the reference is, which was used in the source code at compile time.

Abstraction

As per the dictionary, Abstraction is the quality of dealing with ideas rather than events. For example when you consider the case of e-mail, complex details such as what happens when you send an e-mail, the protocol your email server uses are hidden from the user, therefore to send an e-mail you just need to type the content, mention the address of the receiver and click send.

likewise in Object oriented programming Abstraction is a process of hiding the implementation details from the user, only the functionality will be provided to the user. In other words user will have the information on what the object does instead of how it does it.

In Java Abstraction is achieved using Abstract classes, and Interfaces.

Abstract Class

A class which contains the abstract keyword in its declaration is known as abstract class.

  • Abstract classes may or may not contain abstract methods ie., methods without a body.
    • Un «abstract method» is un método que sólo está declarado. No contiene más nada: public void get();
  • But, if a class has at least one abstract method, then the class must be declared abstract.
  • If a class is declared abstract it cannot be instantiated.
  • To use an abstract class you have to inherit it from another class, provide implementations to the abstract methods in it.
  • If you inherit an abstract class you have to provide implementations to all the abstract methods in it.

Example:

package atest1;

public abstract class Employee {
    
    private String name;
    private String address;
    private int    number;
    
    public Employee(String name, String address, int number){
        System.out.println("Constructing an Employee");
        this.name    = name;
        this.address = address;
        this.number  = number;
    }
    
    public double computePay(){
        System.out.println("Inside Employee computePay");
        return 0.0;
    }
    
    public void mailCheck(){
        System.out.println("Mailing a check to " + this.name + " " + this.address);
    }
    
    public String toString(){
        return name + " " + address + " " + number;
    }
    
    public String getName(){
        return name;
    }
    
    public String getAddress(){
        return address;
    }
    
    public void setAddress(String newAddress){
        address = newAddress;
    }
    
    public int getNumber(){
        return number;
    }
            
}

You can observe that except abstract methods the Employee class is same as normal class in Java. The class is now abstract, but it still has three fields, seven methods, and one constructor.

Now you can try to instantiate the Employee class as shown below:

package atest1_abstract_class;

public class AbstractDemo {
    
    public static void main(String [] args){
        // Following is not allowed and would raise the error:
        // Employee.java:46: Employee is abstract; cannot be instantiated
        // Employee e = new Employee("George W.", "Houston, TX", 43);
        
        // Esto no s'e que es. Netbeans me da la opci'on de instanciar esta abstract class as'i:
        Employee e = new Employee("George W.", "Houston, TX", 43) {};
        
        System.out.println("\n Call mailCheck using Employee reference:");
        e.mailCheck();
        
    }
    
}

When you compile the above class, it gives you the following error:

Employee.java:46: Employee is abstract; cannot be instantiated
    Employee e = new Employee("George W.", "Houston, TX", 43);
Inheriting the Abstract Class

We can inherit the properties of Employee class just like concrete class as shown below:

package atest1_abstract_class;

public class Salary extends Employee{
    
    private double salary;
    
    public Salary(String name, String address, int number, double salary){
        super(name, address, number);
        setSalary(salary);
    }
    
    public void mailCheck(){
        System.out.println("Within mailCheck of Salary class ");
        System.out.println("Mailing check to " + getName() + " with salary " + salary);
    }
    
    public double getSalary(){
        return salary;
    }
    
    public void setSalary(double newSalary){
        if(newSalary >= 0.0){
            salary = newSalary;
        }
    }
    
    public double computePay(){
        System.out.println("Computing salary pay for " + getName());
        return salary/52;
    }
}

Here, you cannot instantiate the Employee class, but you can instantiate the Salary Class, and using this instance you can access the all the three fields and seven methods of Employee class as shown below.

package atest1_abstract_class;

public class AbstractDemo {
    
    public static void main(String [] args){  
        Salary   s  = new Salary("Joe Bloggs", "Dublin 10", 3, 3600.00);
        Employee e2 = new Salary("John Adams", "Dublin 12", 2, 2400.00);
        
        System.out.println("\nCall mailCheck using Salary reference:");
        s.mailCheck();
        
        System.out.println("\nCall mailCheck using Employee reference--");
        e.mailCheck();
        
    }
    
}

This produces the following result:

Constructing an Employee
Constructing an Employee

Call mailCheck using Salary reference:
Within mail Check of Salary class
Mailing check to Joe Bloggs with salary 3600.0

Call mail Check using Employee reference:
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.00

Abstract Methods

If you want a class to contain a particular method but you want the actual implementation of that method to be determined by child classes, you can declare the method in the parent class as abstract.

  • Abstract keyword is used to declare the method as abstract.
  • You have to place the abstract keyword before the method name in the method declaration.
  • An abstract method contains a method signature, but no method body.
public abstract class Employee{

    private String name;
    private String address;
    private int number;

    public abstract double computePay();

    // Remainder of class definition

}

Declaring a method as abstract has two consequences:

  • The class containing it must be declared as abstract.
  • Any class inheriting the current class must either override the abstract method or declare itself as abstract.

Suppose Salary class inherits the Employee class, then it should implement the computePay() method as shown below:

public class Salary extends Employee {
    private double salary;
    public double computePay() {
        System.out.println("Computing salary pay for " + getName());
        return salary/52;
    }

    // Remainder of class definition

}

Nested classes

The Java programming language allows you to define a class within another class. Such a class is called a nested class and is illustrated here:

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}


Compelling reasons for using nested classes include the following:

  • It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Thus, having that functionality contained within a nested class will increase readability and reduce clutter.
  • It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
Another very important advantage of having nested classes, is the fact that nested classes will be able to access all the class members of their enclosing top-level-class (including the private member methods and variables). This is a more efficient way than the traditional way of accessing the non-private members using an object instance. A noticeable advantage of this feature can be seen when developing event-based applications, where action and event listeners can be defined as nested classes rather than having them as separate individual top-level-classes.
  • It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.
  • A difference or an advantage nested classes possess over subclasses, is that subclasses can't access private members (unless they are declared as protected) while nested classes can.


Nested classes in java can be classified into the following types:

  • non-static nested classes
  • static nested classes

non-static nested classes

non-static nested classes are defined within the top-level-classes and they are bound by the scope of their enclosing class. non-static nested classes can have direct access into all of the members (event the private ones) of their enclosing class. On the other hand, the enclosing class doesn't have any access to the members of the enclosed nested class. It is also worth to mention that nested classes (in general) can be defined from within any block inside a top-level-class, that could be a method block or even a control statement target block (such as if, for or while). There are two flavours of non-static nested classes:

Inner Classes

These are the most widely used type of nested classes, the following code excerpt shows:

  • How to define a an inner class within a normal top-level-class
  • How easily the private members of the enclosing class can be accessed by the nested class
  • How to obtain an instance of the inner class from within the enclosing class
package innerclass;

public class OuterClass {
    
    private int private_member_variable = 100;
    
    public class InnerClass {
        public void printPrivateVariable(){
            System.out.println(private_member_variable);
        }
    }
    
    public void callInnerClassMethod(){
        InnerClass innerClass = new InnerClass();
        innerClass.printPrivateVariable();
    }
    
    public static void main(String args[]) {
        OuterClass outerClass = new OuterClass();
        outerClass.callInnerClassMethod();
    }
    
}

The following code explains how an instance of the Inner class can be obtained from code outside their enclosing class:

public class TestClass {
    public static void main(String args[]) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.printPrivateVariable();
    }
}

Anonymous Classes

static nested classes

Upcasting - Downcasting