Tag: java

জাভা গেটার এবং সেটার মেথড (Java Getter and Setter)

1. What are getter and setter?

In Java, getter and setter are two conventional methods that are used for retrieving and updating value of a variable.The following code is an example of simple class with a private variable and a couple of getter/setter methods:

public class SimpleGetterAndSetter {
    private int number;
    public int getNumber() {
        return this.number;
    }
    public void setNumber(int num) {
        this.number = num;
    }
}
The class declares a private variable, number. Since number is private, code from outside this class cannot access the variable directly, like this:
SimpleGetterAndSetter obj = new SimpleGetterAndSetter();
obj.number = 10;    // compile error, since number is private
int num = obj.number; // same as above

Instead, the outside code have to invoke the getter, getNumber() and the setter, setNumber() in order to read or update the variable, for example:

1
2
3
4
SimpleGetterAndSetter obj = new SimpleGetterAndSetter();
obj.setNumber(10);  // OK
int num = obj.getNumber();  // fine

So, a setter is a method that updates value of a variable. And a getter is a method that reads value of a variable.

Getter and setter are also known as accessor and mutator in Java.

2. Why getter and setter?

By using getter and setter, the programmer can control how his important variables are accessed and updated in a correct manner, such as changing value of a variable within a specified range. Consider the following code of a setter method:

1
2
3
4
5
6
public void setNumber(int num) {
    if (num < 10 || num > 100) {
        throw new IllegalArgumentException();
    }
    this.number = num;
}

That ensures the value of number is always set between 10 and 100.  Suppose the variable number can be updated directly, the caller can set any arbitrary value to it:

1
obj.number = 3;

And that violates the constraint for values ranging from 10 to 100 for that variable. Of course we don’t expect that happens. Thus hiding the variable number as private and using a setter comes to rescue.On the other hand, a getter method is the only way for the outside world reads the variable’s value:

1
2
3
public int getNumber() {
    return this.number;
}

The following picture illustrates the situation:getter and setterSo far, setter and getter methods protect a variable’s value from unexpected changes by outside world – the caller code.When a variable is hidden by private modifier and can be accessed only through getter and setter, it is encapsulated. Encapsulation is one of the fundamental principles in object-oriented programming (OOP), thus implementing getter and setter is one of ways to enforce encapsulation in program’s code.Some frameworks such as Hiberate, Spring, Struts… can inspect information or inject their utility code through getter and setter. So providing getter and setter is necessary when integrating your code with such frameworks.

3. Naming convention for getter and setter

The naming scheme of setter and getter should follow Java bean naming convention as follows:

getXXX() and setXXX()

where XXX is name of the variable. For example with the following variable name:

1
private String name;

then the appropriate setter and getter will be:

1
2
3
public void setName(String name) { }
public String getName() { }

If the variable is of type boolean, then the getter’s name can be either isXXX() or getXXX(), but the former naming is preferred. For example:

1
2
3
private boolean single;
public String isSingle() { }

The following table shows some examples of getters and setters which are qualified for naming convention:

Variable declaration Getter method Setter method
int quantity int getQuantity() void setQuantity(int qty)
string firstName String getFirstName() void setFirstName(String fname)
Date birthday Date getBirthday() void setBirthday(Date bornDate)
boolean rich boolean isRich()

boolean getRich()

void setRich(Boolean rich)

 

4. Common mistakes when implementing getter and setter

People often make mistakes, so do developers. This section describes the most common mistakes when implementing setter and getter in Java, and workarounds.

Mistake #1: Have setter and getter, but the variable is declared in less restricted scope.

Consider the following code snippet:

1
2
3
4
5
6
7
8
9
public String firstName;
public void setFirstName(String fname) {
    this.firstName = fname;
}
public String getFirstName() {
    return this.firstName;
}

The variable firstName is declared as public, so it can be accessed using dot (.) operator directly, making the setter and getter useless. Workaround for this case is using more restricted access modifier such as protected and private:

1
private String firstName;

 

Mistake #2: Assign object reference directly in setter

Considering the following setter method:

1
2
3
4
5
private int[] scores;
public void setScores(int[] scr) {
    this.scores = scr;
}

And following is code that demonstrates the problem:

1
2
3
4
5
6
7
8
9
int[] myScores = {554324};
setScores(myScores);
displayScores();   
myScores[1] = 1;
displayScores();

An array of integer numbers myScores is initialized with 6 values (line 1) and the array is passed to the setScores() method (line 2). The method displayScores() simply prints out all scores from the array:

1
2
3
4
5
6
public void displayScores() {
    for (int i = 0; i < this.scores.length; i++) {
        System.out.print(this.scores[i] + " ");
    }
    System.out.println();
}

Line 3 will produce the following output:

1
5    5   4   3   2   4

That are exactly all elements of the myScores array.Now at line 4, we can modify the value of the 2nd element in the myScores array as follow:

1
myScores[1] = 1;

What will happen if we call the method displayScores() again at line 5? Well, it will produce the following output:

1
5    1   4   3   2   4

You can realize that the value of 2nd element is changed from 5 to 1, as a result of the assignment in line 4. Why does it matter? Well, that means the data can be modified outside scope of the setter method which breaks encapsulation purpose of the setter. And why that happens? Let’s look at the setScores() method again:

1
2
3
public void setScores(int[] scr) {
    this.scores = scr;
}

The member variable scores is assigned to the method’s parameter variable scr directly. That means both the variables are referring the same object in memory – the myScores array object. So changes made to either scores variable or myScoresvariable are actually made on the same object.Workaround for this situation is to copy elements from scr array to scores array, one by one. The modified version of the setter would be like this:

1
2
3
4
public void setScores(int[] scr) {
    this.scores = new int[scr.length];
    System.arraycopy(scr, 0this.scores, 0, scr.length);
}

What’s difference? Well, the member variable scores is no longer referring to the object referred by scr variable. Instead, the array scores is initialized to a new one with size equals to the size of the array scr. Then we copy all elements from the array scr to the array scores, using System.arraycopy() method.Run the example again and it will give the following output:

1
2
5    5   4   3   2   4
5    5   4   3   2   4

Now the two invocation of displayScores()produce the same output. That means the array scores is independent and different than the array scr passed into the setter, thus the assignment:

1
myScores[1] = 1;

does not affect the array scores.So, the rule of thumb is, if you pass an object reference into a setter method, then don’t copy that reference into the internal variable directly. Instead, you should find some ways to copy values of the passed object into the internal object, like we have copied elements from one array to another using System.arraycopy() method.

Mistake #3: Return object reference directly in getter

Consider the following getter method:

1
2
3
4
5
private int[] scores;
public int[] getScores() {
    return this.scores;
}

And the following code snippet:

1
2
3
4
5
6
7
8
9
10
11
int[] myScores = {554324};
setScores(myScores);
displayScores();
int[] copyScores = getScores();
copyScores[1] = 1;
displayScores();

it will produce the following output:

1
2
5    5   4   3   2   4
5    1   4   3   2   4

As you notice, the 2nd element of the array scores is modified outside the setter, at line 5. Because the getter method returns reference of the internal variable scores directly, so the outside code can obtain this reference and makes change to the internal object.Workaround for this case is that, instead of returning the reference directly in the getter, we should return a copy of the object, so the outside code can obtain only a copy, not the internal object. Therefore we modify the above getter as follows:

1
2
3
4
5
public int[] getScores() {
    int[] copy = new int[this.scores.length];
    System.arraycopy(this.scores, 0, copy, 0, copy.length);
    return copy;
}

So the rule of thumb is: do not return reference of the original object in getter method. Instead, it should return a copy of the original object.

5. Implementing getters and setters for primitive types

With primitive types (int, float, double, boolean, char…), you can freely assign/return values directly in setter/getter because Java copies value of one primitive to another instead of copying object reference. So the mistakes #2 and #3 can be avoided.For example, the following code is safe because the setter and getter involve in a primitive type of float:

1
2
3
4
5
6
7
8
9
private float amount;
public void setAmount(float amount) {
    this.amount = amount;
}
public float getAmount() {
    return this.amount;
}

So, for primitive types, there is no special trick to correctly implement the getter and setter.

6. Implementing getters and setters for common object types

Getters and Setters for String objects

String is an object type, but it is immutable which means once a String object is created, its String literal cannot be changed. In other words, every change on that String object will result in a new String object created. So, like primitive types, you can safely implement getter and setter for a String variable like this:

1
2
3
4
5
6
7
8
9
private String address;
public void setAddress(String addr) {
    this.address = addr;
}
public String getAddress() {
    return this.address;
}

 

Getters and Setters for Date objects

The java.util.Date class implements clone() method from the Object class. The method clone() return a copy of the object, so we can use the clone()  method for the getter and setter, like the following example:

1
2
3
4
5
6
7
8
9
private Date birthDate;
public void setBirthDate(Date date) {
    this.birthDate = (Date) date.clone();
}
public Date getBirthDate() {
    return (Date) this.birthDate.clone();
}

The clone()method returns an Object, so we must cast it to Date type.

7. Implementing getters and setters for collection type

As described in mistake #2 and mistake #3, it’s not good to have setter and getter methods like this:

1
2
3
4
5
6
7
8
9
private List<String> listTitles;
public void setListTitles(List<String> titles) {
    this.listTitles = titles;
}
public List<String> getListTitles() {
    return this.listTitles;
}

Consider the following program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.util.*;
public class CollectionGetterSetter {
    private List<String> listTitles;
    public void setListTitles(List<String> titles) {
this.listTitles = titles;
    }
    public List<String> getListTitles() {
        return this.listTitles;
    }
    public static void main(String[] args) {
        CollectionGetterSetter app = new CollectionGetterSetter();
        List<String> titles1 = new ArrayList();
        titles1.add("Name");
        titles1.add("Address");
        titles1.add("Email");
        titles1.add("Job");
        app.setListTitles(titles1);
        System.out.println("Titles 1: " + titles1);
        titles1.set(2"Habilitation");
        List<String> titles2 = app.getListTitles();
        System.out.println("Titles 2: " + titles2);
        titles2.set(0"Full name");
        List<String> titles3 = app.getListTitles();
        System.out.println("Titles 3: " + titles3);
    }
}

According to the rules for implementing getter and setter, the three System.out.println() statements should produce the same result. However, when running the above program it produces the following output:

1
2
3
Titles 1: [Name, Address, Email, Job]
Titles 2: [Name, Address, Habilitation, Job]
Titles 3: [Full name, Address, Habilitation, Job]

That means the collection can be modified from code outside of the getter and setter.For a collection of Strings, one solution is to use the constructor that takes another collection as argument, for example we can change code of the above getter and setter as follows:

1
2
3
4
5
6
7
public void setListTitles(List<String> titles) {
    this.listTitles = new ArrayList<String>(titles);
}
public List<String> getListTitles() {
    return new ArrayList<String>(this.listTitles);   
}

Re-compile and run the CollectionGetterSetter program, it will produce desired output:

1
2
3
Titles 1: [Name, Address, Email, Job]
Titles 2: [Name, Address, Email, Job]
Titles 3: [Name, Address, Email, Job]

NOTE: The constructor approach above is only working with collections of Strings, but it will not work for collections objects. Consider the following example for a collection of Person object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.util.*; 
  
public class CollectionGetterSetterObject { 
    private List<Person> listPeople; 
  
    public void setListPeople(List<Person> list) { 
        this.listPeople = new ArrayList<Person>(list); 
    
  
    public List<Person> getListPeople() { 
        return new ArrayList<Person>(this.listPeople); 
    
  
    public static void main(String[] args) { 
        CollectionGetterSetterObject app = new CollectionGetterSetterObject(); 
  
        List<Person> list1 = new ArrayList<Person>(); 
        list1.add(new Person("Peter")); 
        list1.add(new Person("Alice")); 
        list1.add(new Person("Mary")); 
  
        app.setListPeople(list1); 
  
        System.out.println("List 1: " + list1); 
  
        list1.get(2).setName("Maryland"); 
  
        List<Person> list2 = app.getListPeople(); 
        System.out.println("List 2: " + list2); 
  
        list1.get(0).setName("Peter Crouch"); 
  
        List<Person> list3 = app.getListPeople(); 
        System.out.println("List 3: " + list3); 
  
    
  
class Person { 
    private String name; 
  
    public Person(String name) { 
        this.name = name; 
    
  
    public String getName() { 
        return this.name; 
    
  
    public void setName(String name) { 
        this.name = name; 
    
  
    public String toString() { 
        return this.name; 
    
}

It produces the following output when running:

1
2
3
List 1: [Peter, Alice, Mary]
List 2: [Peter, Alice, Maryland]
List 3: [Peter Crouch, Alice, Maryland]

Because unlike String which new objects will be created whenever a String object is copied, other Object types are not. Only references are copied, so that’s why two collections are distinct but they contain same objects. In other words, it is because we haven’t provided any mean for copying objects.Look at the collection API we found that ArrayList, HashMap, HashSet, … implement their own clone() methods, these methods return shallow copies which do not copy elements from the source collection to the destination. According to Javadoc of the clone() method of ArrayList class:

1
2
public Object clone()
Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)

Thus we cannot use the clone()method of these collection classes. The solution is to implement the clone()method for our own defined object – the Person class in the above example. We implement the clone()method in the Person class as follows:

1
2
3
4
public Object clone() {
    Person aClone = new Person(this.name);
    return aClone;
}

The setter for listPeople is modified as follows:

1
2
3
4
5
public void setListPeople(List<Person> list) {
    for (Person aPerson : list) {
        this.listPeople.add((Person) aPerson.clone());
    }
}

The corresponding getter is modified as follows:

1
2
3
4
5
6
7
8
public List<Person> getListPeople() {
    List<Person> listReturn = new ArrayList<Person>();
    for (Person aPerson : this.listPeople) {
        listReturn.add((Person) aPerson.clone());
    }
    return listReturn;
}

That results in a new version of the class CollectionGetterSetterObject as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.*;
public class CollectionGetterSetterObject {
    private List<Person> listPeople = new ArrayList<Person>();
    public void setListPeople(List<Person> list) {
        for (Person aPerson : list) {
            this.listPeople.add((Person) aPerson.clone());
        }
    }
    public List<Person> getListPeople() {
        List<Person> listReturn = new ArrayList<Person>();
        for (Person aPerson : this.listPeople) {
            listReturn.add((Person) aPerson.clone());
        }
        return listReturn;
    }
    public static void main(String[] args) {
        CollectionGetterSetterObject app = new CollectionGetterSetterObject();
        List<Person> list1 = new ArrayList<Person>();
        list1.add(new Person("Peter"));
        list1.add(new Person("Alice"));
        list1.add(new Person("Mary"));
        app.setListPeople(list1);
        System.out.println("List 1: " + list1);
        list1.get(2).setName("Maryland");
        List<Person> list2 = app.getListPeople();
        System.out.println("List 2: " + list2);
        list1.get(0).setName("Peter Crouch");
        List<Person> list3 = app.getListPeople();
        System.out.println("List 3: " + list3);
    }
}

Compile and run the new version of CollectionGetterSetterObject, it will produce the desired output:

1
2
3
List 1: [Peter, Alice, Mary]
List 2: [Peter, Alice, Mary]
List 3: [Peter, Alice, Mary

So key points for implementing getter and setter for collection type are:

  • For collection of String objects: does not need any special tweak since String objects are immutable.
  • For collection of custom types of object:
    • Implement clone() method for the custom type.
    • For the setter, add cloned items from source collection to the destination one.
    • For the getter, create a new collection which is being returned. Add cloned items from the original collection to the new one.

 

 

8. Implementing getters and setters for your own type

If you define a custom type of object, you should implement clone() method for your own type. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String toString() {
        return this.name;
    }
    public Object clone() {
        Person aClone = new Person(this.name);
        return aClone;
    }
}

As we can see, the class Person implements its clone() method to return a cloned version of itself. Then the setter method should be implemented as follows:

1
2
3
public void setFriend(Person person) {
    this.friend = (Person) person.clone();
}

And for the getter method:

1
2
3
public Person getFriend() {
    return (Person) this.friend.clone();
}

So the rules for implementing getter and setter for a custom object type are:

  • Implement clone() method for the custom type.
  • Return a cloned object from the getter.
  • Assign a cloned object in the setter
Advertisements

জাভা সুপার কিওয়ার্ড (Java super Keyword)

আমরা যদি সাব ক্লাস থেকে সুপার ক্লাসের কোন মেথড বা ভেরিয়েবল একসেস করতে চাই তাহলে আমরা এই কিওয়ার্ডটি ব্যবহার করি। কিন্তু আমরা জানি যে সাব ক্লাসে অটোমেটিক্যালি সুপার ক্লাসের সব প্রোপ্রার্টি চলে আসে তাহলে এর প্রয়োজনিয়তা নিয়ে প্রশ্ন হতে পারে।

আমরা আবার উপরের Cylinder ক্লাসটি আবার দেখি। এই ক্লাসটিতে আমরা নতুন আরেকটি মেথড লিখতে চাই।

      public double getVolume() {
            return getArea() * height;
        }

এই মেথডটি-তে getArea() * height এই স্ট্যাটমেন্টটি লক্ষ করি। এখানে getArea() এই মেথটি কল করা হয়েছে। আমাদের এই Cylinder ক্লাসটিতে getArea() মেথডটিকে আমরা Circle ক্লাস এর getArea()মেথড-কে অভারাইড করেছি। সুতরাং আমরা যখন এই Cylinder ক্লাস থেকে getArea() মেথডটি কল করবো তখন আসলে Cylinder ক্লাস এর মেথডটি কল হবে।

কিন্তু এক্ষেত্রে আমাদের একটি সমস্যা হচ্ছে যে – আমরা জানি সিলিন্ডারের আয়তন

V  = Pi * r^2 * h
    =  (Pi * r^2) * h
    =  Area of Circle * h 

সুতরাং Cylinder ক্লাসের ‌getArea() মেথডটি ব্যবহার করা যাচ্ছে না। কারণ সিলিন্ডারের ক্ষেত্রফল-

        A = (2 * Pi  * r * h)  +  (2 * Pi * r^2)

কিন্তু আমরা যদি Circle ক্লাস এর মেথডটি ব্যবহার করি তাহলে আমাদের সমস্যা সমাধান হয়ে যায়। এখন যদি আমরা সুপার ক্লাস(Circle) এর মেথডটি কল করে এই মেথডটি লিখতে চাই তাহলে –

     public double getVolume() {
            return super.getArea() * height;
     }

অর্থাৎ সাব ক্লাসে যদি মেথড অভাররাইড করা হয় এবং তারপরেও কোন কারণে যদি আমাদের সুপার ক্লাসের মেথড কে কল করার প্রয়োজন হয় তাহলে আমরা সুপার(super) কিওয়ার্ডটি ব্যবহার করি।

ইনহেরিটেন্স এর ক্ষেত্রে মনে রাখতে হবে –

জাভা মাল্টিপল ইনহেরিটেন্স সাপোর্ট করে না। এর মানে হচ্ছে আমার একটি ক্লাস শুধুমাত্র একটি ক্লাসকেই ইনহেরিট করতে পারে।

Usage of java super Keyword

  1. super can be used to refer immediate parent class instance variable.
  2. super can be used to invoke immediate parent class method.
  3. super() can be used to invoke immediate parent class constructor.

1) super is used to refer immediate parent class instance variable.

We can use super keyword to access the data member or field of parent class. It is used if parent class and child class have same fields.

class Animal{

String color=“white”;

}

class Dog extends Animal{

String color=“black”;

void printColor(){

System.out.println(color);//prints color of Dog class

System.out.println(super.color);//prints color of Animal class

}

}

class TestSuper1{

public static void main(String args[]){

Dog d=new Dog();

d.printColor();

}}

Test it Now

Output:

black
white

In the above example, Animal and Dog both classes have a common property color. If we print color property, it will print the color of current class by default. To access the parent property, we need to use super keyword.

2) super can be used to invoke parent class method

The super keyword can also be used to invoke parent class method. It should be used if subclass contains the same method as parent class. In other words, it is used if method is overridden.

class Animal{

void eat(){System.out.println(“eating…”);}

}

class Dog extends Animal{

void eat(){System.out.println(“eating bread…”);}

void bark(){System.out.println(“barking…”);}

void work(){

super.eat();

bark();

}

}

class TestSuper2{

public static void main(String args[]){

Dog d=new Dog();

d.work();

}}

Test it Now

Output:

eating...
barking...

In the above example Animal and Dog both classes have eat() method if we call eat() method from Dog class, it will call the eat() method of Dog class by default because priority is given to local.

To call the parent class method, we need to use super keyword.

3) super is used to invoke parent class constructor.

The super keyword can also be used to invoke the parent class constructor. Let’s see a simple example:

class Animal{

Animal(){System.out.println(“animal is created”);}

}

class Dog extends Animal{

Dog(){

super();

System.out.println(“dog is created”);

}

}

class TestSuper3{

public static void main(String args[]){

Dog d=new Dog();

}}

Test it Now

Output:

animal is created
dog is created

Note: super() is added in each class constructor automatically by compiler if there is no super() or this().

java super

As we know well that default constructor is provided by compiler automatically if there is no constructor. But, it also adds super() as the first statement.

Another example of super keyword where super() is provided by the compiler implicitly.

class Animal{

Animal(){System.out.println(“animal is created”);}

}

class Dog extends Animal{

Dog(){

System.out.println(“dog is created”);

}

}

class TestSuper4{

public static void main(String args[]){

Dog d=new Dog();

}}

Test it Now

Output:

animal is created
dog is created

super example: real use

Let’s see the real use of super keyword. Here, Emp class inherits Person class so all the properties of Person will be inherited to Emp by default. To initialize all the property, we are using parent class constructor from child class. In such way, we are reusing the parent class constructor.

class Person{

int id;

String name;

Person(int id,String name){

this.id=id;

this.name=name;

}

}

class Emp extends Person{

float salary;

Emp(int id,String name,float salary){

super(id,name);//reusing parent constructor

this.salary=salary;

}

void display(){System.out.println(id+” “+name+” “+salary);}

}

class TestSuper5{

public static void main(String[] args){

Emp e1=new Emp(1,“ankit”,45000f);

e1.display();

}}

Test it Now

Output:

1 ankit 45000

জাভা মেথড ওভাররাইডিং(Java Method Overriding)

Usage of Java Method Overriding

  • Method overriding is used to provide specific implementation of a method that is already provided by its super class.
  • Method overriding is used for runtime polymorphism

Rules for Java Method Overriding

  1. method must have same name as in the parent class
  2. method must have same parameter as in the parent class.
  3. must be IS-A relationship (inheritance).

Understanding the problem without method overriding

Let’s understand the problem that we may face in the program if we don’t use method overriding.

যদিও সাব-ক্লাস সুপার-ক্লাসের সব গুলো প্রোপ্রার্টি এবং মেথড ইনহেরিটর করে, তবে সাব-ক্লাসে সুপার ক্লাসের যে কোন প্রোপ্রার্টি বা মেথড কে অভাররাইড করা যায়।

একটি উদাহরণ দেখা যাক-

      public class Circle {
          double radius;
          String color;

          public Circle(double radius, String color) {
              this.radius = radius;
              this.color = color;
          }

          public Circle() {
              radius = 1.0;
              color = "RED";
          }

          public double getArea() {
              return radius * radius * Math.PI;
          }
      }

এই ক্লাসটিতে getArea() মেথড একটি বৃত্তের ক্ষেত্রফল রিটার্ন করে।

এখন আমরা এই ক্লাসটিকে এক্সটেন্ড(‌‌‌‌‌extends‌) করে নতুন আরেকটি ক্লাস লিখবো-

    public class Cylinder extends Circle {
        double height;

        public Cylinder() {
            this.height = 1.0;
        }

        public Cylinder(double radius, String color, double height) {
            super(radius, color);
            this.height = height;
        }

      @Override    
        public double getArea() {
            return 2 * Math.PI * radius * height + 2 * super.getArea();
        }
    }

এই ক্লাসটিতে Circle এর মেথডটি আমরা সাধারণ ভাবেই পেয়ে যাবো। Cylinder এর ক্ষেত্রফল নির্ধারণ করতে হলে getArea() কল করলেই হয়ে যাচ্ছে। কিন্তু আমরা জানি যে Circle এবং Cylinder এর ক্ষেত্রফল একভাবে নির্ধারণ করা যায় না। এক্ষেত্রে আমরা যদি Circle এর মেথডটি কে ব্যবহার করি তাহলে আমাদের ক্ষেত্রফলের মান ভুল আসবে। এই সমস্যা সমধাণ করার জন্যে আমরা আমাদের Cylinder ক্লাসটিতে getArea()মেথডটিকে পুনারায় লিখেছি।

এখানে লক্ষ্য রাখতে হবে যে, দুটি মেথড এর সিগনেচার, রিটার্ন-টাইপ এবং প্যারামিটার লিস্ট একই রকম হতে হবে।

এখন আমরা যদি Cylinder ক্লাস-এর getArea() মেথড কল করি, তাহলে অভারাইডেড মেথডটি কল হবে।

অ্যনোটেশান(Annotation) @Override

@Override এই অ্যনোটেশানটি জাভা 1.5 ভার্সনে প্রথম নিয়ে আসা হয়। কোন মেথডকে যদি আমরা অভাররাইড করি তাহলে সেই মেথড এর উপরে @Override দেয়া হয়। এটি কম্পাইলারকে ইনফর্ম করে যে, এই মেথটি সুপার ক্লাসের অভাররাইডেড মেথড।

তবে এটি অপশনাল হলেও অবশ্যই ভাল যদি ব্যবহার করা হয়।

Real example of Java Method Overriding

Consider a scenario, Bank is a class that provides functionality to get rate of interest. But, rate of interest varies according to banks. For example, SBI, ICICI and AXIS banks could provide 8%, 7% and 9% rate of interest.

Java method overriding example of bank

  1. class Bank{
  2. int getRateOfInterest(){return 0;}
  3. }
  4. class SBI extends Bank{
  5. int getRateOfInterest(){return 8;}
  6. }
  7. class ICICI extends Bank{
  8. int getRateOfInterest(){return 7;}
  9. }
  10. class AXIS extends Bank{
  11. int getRateOfInterest(){return 9;}
  12. }
  13. class Test2{
  14. public static void main(String args[]){
  15. SBI s=new SBI();
  16. ICICI i=new ICICI();
  17. AXIS a=new AXIS();
  18. System.out.println(“SBI Rate of Interest: “+s.getRateOfInterest());
  19. System.out.println(“ICICI Rate of Interest: “+i.getRateOfInterest());
  20. System.out.println(“AXIS Rate of Interest: “+a.getRateOfInterest());
  21. }
  22. }

Test it Now

Output:
SBI Rate of Interest: 8
ICICI Rate of Interest: 7
AXIS Rate of Interest: 9

Can we override static method?

No, static method cannot be overridden. It can be proved by runtime polymorphism, so we will learn it later.


Why we cannot override static method?

because static method is bound with class whereas instance method is bound with object. Static belongs to class area and instance belongs to heap area.


Can we override java main method?

No, because main is a static method.


Difference between method Overloading and Method Overriding in java

Click me for difference between method overloading and overriding


জাভা পলিমরফিজম ( Java Polymorphism )

এবার আমরা কথা বলবো পলিমরফিজম নিয়ে। শব্দটির মধ্যেই একটি বিশেষ গাম্ভীর্য আছে যা কিনা একটি সাধারণ কথোপকথনকে অনেক গুরুত্বর্পূণ করে তুলতে পারে। তবে এটি অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং এর একটি বহুল ব্যবহৃত কৌশল । এই শব্দটির সহজ মানে হচ্ছে যার একাধিক রূপ আছে অর্থাৎ বহুরূপিতা।

সহজ কথায় পলিমরফিজম হল এমন একটি টেকনিক বা পদ্ধতি যেখানে আমরা একটি ক্লাস, অ্যাবস্ট্রাক্ট ক্লাস বা ইন্টারফেসের অবজেক্ট ক্রিয়েট করি তার চাইল্ড ক্লাসের কনস্ট্রাক্টরের মাধ্যমে । অর্থাৎ আমরা একটি ক্লাসের অবজেক্ট ক্রিয়েট করবো অন্য একটি ক্লাসের কনস্ট্রাক্টর কল করে । সহজ ভাষায় এটিই হল পলিমরফিজম ।

মনে করা যাক, ‌‌ ‌

    public class Liquid {
        public void swirl(boolean clockwise) {
            // Implement the default swirling behavior for liquids
            System.out.println("Swirling Liquid");
        }
    }

এখন এর একটি অবজেক্ট তৈরি করতে চাইলে – আমাদের new অপারেটর ব্যবহার করে তা একটি ভেরিয়েবল এ রাখতে হবে।

        Liquid myFavoriteBeverage = new Liquid ();

এখানে myFavoriteBeverage হচ্ছে আমাদের ভেরিয়েবল যা Liquid অবজেক্ট এর রেফারেন্স। আমরা এখন পর্যন্ত যা যা শিখেছি সে অনুযায়ী এই স্টেন্টমেন্টটি যথার্থ। তবে আমরা এর আগের অধ্যায়ে Is-A সম্পর্কে জেনে এসেছি।

আমাদের জাভা প্রোগ্রামিং পলিমরফিজম সাপোর্ট করায় আমরা myFavoriteBeverage এই রেফারেন্সের যায়গায় Is-A সম্পর্কিত যে কোন টাইপ রাখতে পারি। যেমন – ‌ ‌


    Liquid myFavoriteBeverage = new Coffee();
    Liquid myFavoriteBeverage = new Milk();

এখানে Coffee এবং Milk হচ্ছেLiquidএর সাব- ক্লাস বা টাইপ এবং Liquid এদের সুপার ক্লাস বা টাইপ।

পলিমরফিজম নিয়ে আরও একটু আশ্চর্য হতে চাইলে আমরা এখন একটি বিষয় জানবো যা দিয়ে আমরা কোন একটি অবজেক্ট এর কোন মেথড কল করবো তবে তা কোন ক্লাসের অবজেক্ট সেটি না জেনেই। আরেকটু পরিষ্কার করে বলি, আমরা যখন সুপার ক্লাসের এর রেফারেন্স ধরে কোন এর মেথড কল করবো তখন কিন্তু আমরা জানি না যে এটি আসলে কোন অবজেক্ট এর মেথড। যেমন- ‌ ‌


    Liquid myFavoriteBeverage = // ….

এখানে আমাদের myFavoriteBeverage এই রেফারেন্স এ LiquidCoffeeMilk এর যেকোন একটির অবজেক্ট হতে পারে। উদাহরণ –

    public class Coffee extends Liquid {
        @Override
        public void swirl(boolean clockwise) {
            System.out.println("Swirling Coffee");
        }
    }

    public class Milk extends Liquid{
        @Override
        public void swirl(boolean clockwise) {
            System.out.println("Swirling Milk");
        }
    } 

    public class CoffeeCup {
        private Liquid innerLiquid;

        void addLiquid(Liquid liq) {
            innerLiquid = liq;
            // Swirl counterclockwise
            innerLiquid.swirl(false);
        }
    }

আমরা এখানে একটি CoffeeCup ক্লাস লিখেছি যার মাঝে addLiquid() নামে একটি মেথড আছে যা কিনা একটি Liquid টাইপ parameter নেয়, এবং সেই Liquid এর swirl() মেথড-কে কল করে।

কিন্তু আমরা আমাদের সত্যিকারের জগতে একটি কফি-কাপ এ শুধুমাত্র কফি-ই এড করতে পারি তা নয়, আমরা চাইলে যে কোন ধরণের লিকুইড এড করতে পারি, সেটি মিল্ক ও হতে পারে। তাহলে এই addLiquid মেথড তো শুধুমাত্র Liquid টাইপ parameter নেয়, তাহলে আমাদের সত্যিকারের জগতের সাথে এই প্রোগ্রামিং মডেল এর সাদৃশ্য থাকলো কোথায় ?

তবে মজার ব্যপার এখানেই, আমাদের এই CoffeeCup ক্লাসটি পলিমরফিজমের ম্যজিক ব্যাবহার করে সত্যিকার অর্থেই আমাদের সত্যিকারের জগতের CoffeeCup এর মতোই কাজ করে। ‌


    public class MainApp {
        public static void main(String[] args) {
            // First you need a coffee cup
            CoffeeCup myCup = new CoffeeCup();

            // Next you need various kinds of liquid
            Liquid genericLiquid = new Liquid();
            Coffee coffee = new Coffee();
            Milk milk = new Milk();

            // Now you can add the different liquids to the cup
            myCup.addLiquid(genericLiquid);
            myCup.addLiquid(coffee);
            myCup.addLiquid(milk);
        }
    }

উপরের কোড গুলোতে দেখা যাচ্ছে যে আমরা একটি CoffeeCup এর একটি অবজেক্ট তৈরি করে সেটি তে বিভিন্ন রকম Liquid এড করতে পারছি।

আরেকটু লক্ষ্য করি,

    void addLiquid(Liquid liq) {
            innerLiquid = liq;
            // Swirl counterclockwise
            innerLiquid.swirl(false);
      }

এই মেথডটিতে innerLiquid.swirl(false) যখন কল করি তখন কিন্তু আমরা জানি না যে এই innerLiquid আসলে কোন অবজেক্ট এর রেফারেন্স। এটি লিকুইড বা এর যে কোন সাব-টাইপ হতে পারে।

কিছু প্রয়োজনীয় তথ্য-

১. একটি সাব ক্লাস এর অবজেক্টকে আমরা এর সুপার ক্লাসের রেফারেন্স এ এসাইন করতে পারি। ২. সাব ক্লাসের অবজেক্টকে সুপার ক্লাসের রেফারেন্স-এ এসাইন করলে, মেথড কল করার সময় শুধু মাত্র সুপার ক্লাসের মেথড গুলোকেই কল করতে পারি। ৩. তবে সাব ক্লাস যদি সুপার ক্লাসের মেথড অভাররাইড করে, তাহলে যদিও আমরা সুপার ক্লাস এর রেফারেন্স ধরে মেথড কল করছি, কিন্তু রানটাইম-এ সাব ক্লাসের মেথডটি কল হবে। মনে রাখতে হবে এটি শুধুমাত্র মেথড অভাররাইড করা হলেই সত্য হবে।

আপ-কাস্টিং(Upcasting ) এবং ডাউনকাস্টিং (Downcasting)

        Liquid liquid = new Coffee ();

এখানে সাব ক্লাসের অবজেক্টকে সুপার ক্লাসের রেফারেন্স এ এসাইন করা হয়ছে। একে বলা হয় আপ-কাস্টিং। এই কাস্টিং সবসময় সেইফ ধরা হয় কারণ আপকাস্টং এর ক্ষেত্রে সাব ক্লাস সবসময়ই সুপার ক্লাসের সবকিছু ইনহেরিট করে এবং কম্পাইলার কম্পাইল করার সময়-ই এ কাস্টিং করা সম্ভব কিনা তা চেক করে থাকে।

        Liquid liquid = new String();

উপরের স্টেটমেন্টটি কম্পাইলার কম্পাইল করবে না, কারণ String মোটেই Liquid ক্লাসের সাব ক্লাস নয়। এক্ষেত্রে কম্পাইলার incompatible types ইরর দেখাবে।

হোমেজিনিয়াস কালেকশন ( Homogeneous Collection ):

হোমোজিনিয়াস কালেকশন হল একই ক্লাসের কিছু সংখ্যক অবজেক্টের কালেকশন । একটি উদাহরন দিয়ে বিষয়টি একটু সুরাহা করা যাকঃ

interface Animal {

    public abstract void name(String animalName);
}

class Cow implements Animal {

    private String animalName;

    public void work(String animalWork) {
        System.out.println("Work of " + this.animalName + " is " + animalWork);
    }

    @Override
    public void name(String animalName) {
        this.animalName = animalName;
        System.out.println("Name of the animal is: " + this.animalName);
    }
}

public class Main {

    public static void main(String[] args) {

        Animal[] collection1 = new Cow[3];
        collection1[0] = new Cow();
        collection1[1] = new Cow();
        collection1[2] = new Cow();

        Cow[] collection2 = new Cow[3];
        collection2[0] = new Cow();
        collection2[1] = new Cow();
        collection2[2] = new Cow();
    }
}

লক্ষ করুন । এখানে Cow ক্লাসটি Animal ইন্টারফেসের চাইল্ড । এবং Main ক্লাসের main মেথড এর মাঝে ২ টি অবজেক্টের অ্যারে ডিক্লেয়ার করা হয়েছে । একটি Animal ক্লাসের অবজেক্টের অ্যারে যেটির সবগুলা অবজেক্ট Cow ক্লাসের কনস্ট্রাক্টর দিয়ে ইন্সট্যানশিয়েট করা হয়েছে । এখানে পলিমরফিজম স্পষ্ট । এবং অন্যটি অবজেক্ট অ্যারেটি চীরাচরিত অবজেক্ট অ্যারে । এই দুইি অ্যারেই হল হোমোজিনিয়াস কালেকশনের উদাহরন । বোঝা যায়নি ? ওকে, এখানে collection1 অ্যারেটির প্রতিটি অবজেক্টই Cow ক্লাসের কনস্ট্রাক্টর দিয়ে ইন্সট্যানশিয়েট করা হয়েছে । তার মানে collection1 এর মাঝে সবগুলা অবজেক্টই একই ধরনের । যেহেতু এই অ্যারেটির সবগুলা এলিমেন্ট একই ধরনের/ক্লাসের অবজেক্ট সুতরাং এটিকে বলা হবে হোমোজিনিয়াস কালেকশন । একই কথা collection2 এর ক্ষেত্রেও প্রোযোজ্য ।

হেটারোজিনিয়াস কালেকশন ( Heterogeneous Collection ):

ভিন্নধর্মী অবজেক্টের কালেকশনকেই বলা হয় হেটারোজিনিয়াস কালেকশন । হেটারোজিনিয়াস কালেকশন বুঝতে হলে আমাদের একটি উদাহরন দেখে নেওয়া উত্তমঃ

class Animal {

    String animalName ;
    public Animal(String animalName){
        this.animalName = animalName;
    }
    public void name(){
        System.out.println("Animal name is: "+this.animalName);
    }
}

class Cow extends Animal {

    public Cow(String animalName) {
        super(animalName);
    }

    public void work(String animalWork) {
        System.out.println("Work of " + this.animalName + " is " + animalWork);
    }
}

class Dog extends Animal {

    public Dog(String animalName) {
        super(animalName);
    }

    public void work(String animalWork) {
        System.out.println("Work of " + this.animalName + " is " + animalWork);
    }
}

class Cat extends Animal {

    public Cat(String animalName) {
        super(animalName);
    }

    public void work(String animalWork) {
        System.out.println("Work of " + this.animalName + " is " + animalWork);
    }
}

public class Main {

    public static void main(String[] args) {

        Animal[] animals = new Animal[4];
        animals[0] = new Animal("Dolphin");
        animals[1] = new Cow("Big Cow");
        animals[2] = new Dog("Red Dog");
        animals[3] = new Cat("White Cat");
    }
}

খুব ভালোভাবে লক্ষ করুন । আমরা Animal ক্লাসের অবজেক্টের একটু অ্যারে ডিক্লেয়ার করেছি যার সাইজ ৪ । কিন্তু ইন্সট্যানশিয়েট করার সমস আমরা পলিমরফিজম মেকানিজম ব্যাবহার করে এর চাইল্ড ক্লাসের ভিন্ন ভিন্ন কনস্ট্রাক্টর দিয়ে ইন্সট্যানশিয়েট করেছি । অর্থাৎ animals অ্যারেটির প্রতিটি অবজেক্টই আলাদা আলাদা কনস্ট্রাক্ট দিয়ে ইন্সট্যানশিয়েট করা এবং তাদের বিহ্যাভিয়েরাল পার্থ্য আছে । এধরনের কালেকশনকে বলা হয় হেটারোজিনিয়াস কালেকশন ।

এবার একটু ভিন্ন পন্থায় এগোন যাক । মেইন ক্লাসটিকে আমরা একটু মডিফাই করবো । বাকী সবই ঠিক থাকবে আগের মত ।

public class Main {

    public static void main(String[] args) {

        Animal animal = new Cat("Cute Cat");
        animal.name();
        //animal.work("Some Work");//Not possible
        Cat cat = new Cat("Preety Cat");
        cat.name();
        cat.work("It plays");
    }
}

খেয়াল করে দেখুন আমরা Animal এবং Cat এর অবজেক্ট ক্রিয়েট করার সময় কনস্ট্রাক্টর ব্যাবহার করেছি Catএর কিন্তু Animal এর অবজেক্ট থেকে আমরা work মেথডটি কোন ভাবেই কল করতে পারছি না বা পারবো না কিন্তু Cat এর অবজেক্ট থেকে ঠিকই পারছি । কারনটা কি ? কারন হল Animal ক্লাসের মাঝে ঠিক যে যে মেথড আছে সেগুলাকেই আমরা অ্যাক্সেস করতে পারব তবে Cat এর ইমপ্লিমেন্টেশন দিয়ে । Animal এর মাঝে নেই কিন্তু Cat ক্লাসে বাড়তি আছে এমন কোন মেথডকে আমরা অ্যাক্সেস করতে পারবো না । এমনকি Animalক্লাসের অবজেক্টে Cat ক্লাসের work মেথডের কোন রেফারেন্সই ক্রিয়েট হবেনা ।

তাহলে এটা করি কেন আমরা ? এটা করার পেছনে বেশ কিছু কারন থাকতে পারে । প্রথমত আমরা প্যারেন্ট ক্লাস এবং চাইল্ড ক্লাসের ইমপ্লিমেন্টশন নিয়ে কাজ করতে চাইলে পলিমরফিজমের এই সুবিধাটি নেওয়া হয় । অন্য কারনটি হল মেমোরি কনজাম্পশন । ভেবে দেখুন যদি Animal ক্লাসে ৩ টি মেথড থাকে যেগুলার জন্য আপনি Cat ক্লাসের ইমপ্লিমেন্টেশন ব্যাবহার করতে চান , কিন্তু Cat ক্লাসের মাঝে ১৫ টির মত মেথড আছে এবং অনেক অ্যাট্রিবিউট । আপনি যদি Cat এর অবজেক্ট ক্রিয়েট করেন তবে মেমোরি থেকে প্রচুর স্পেস কনজিউম করবে উক্ত অবজেক্ট । অন্যদিকে আপনি যদি Animal এর অবজেক্ট ক্রিয়েট করেন Cat এর কনস্ট্রাক্টর ব্যাবহার করে তাহলে Cat ক্লাসের ইমপ্লিমেন্টেশন ব্যাবহার করতে পারছেন এবং মেমোরি থেকে খুব কম মেমোরি কনজিউম করছে ( Animal মেথডগুলার জন্য প্রয়োজনীয় মোমোরি মাত্র ) । কোনটি বেশি সুবিধাজনক ? এছাড়া আরো কারন আছে । পরবর্তীতে সেগুলা নিয়েও আলোচনা করা হবে ।

Runtime Polymorphism in Java

Runtime polymorphism or Dynamic Method Dispatch is a process in which a call to an overridden method is resolved at runtime rather than compile-time.

In this process, an overridden method is called through the reference variable of a superclass. The determination of the method to be called is based on the object being referred to by the reference variable.

Let’s first understand the upcasting before Runtime Polymorphism.

Upcasting

When reference variable of Parent class refers to the object of Child class, it is known as upcasting. For example:

Upcasting in java

  1. class A{}
  2. class B extends A{}
  1. A a=new B();//upcasting

Example of Java Runtime Polymorphism

In this example, we are creating two classes Bike and Splendar. Splendar class extends Bike class and overrides its run() method. We are calling the run method by the reference variable of Parent class. Since it refers to the subclass object and subclass method overrides the Parent class method, subclass method is invoked at runtime.

Since method invocation is determined by the JVM not compiler, it is known as runtime polymorphism.

class Bike{

void run(){System.out.println(“running”);}

}

class Splender extends Bike{

void run(){System.out.println(“running safely with 60km”);}

 

public static void main(String args[]){

Bike b = new Splender();//upcasting

b.run();

}

}

Test it Now

Output:running safely with 60km.

Java Runtime Polymorphism Example: Bank

Consider a scenario, Bank is a class that provides method to get the rate of interest. But, rate of interest may differ according to banks. For example, SBI, ICICI and AXIS banks are providing 8.4%, 7.3% and 9.7% rate of interest.

Java Runtime Polymorphism example of bank

Note: This example is also given in method overriding but there was no upcasting.

class Bank{

float getRateOfInterest(){return 0;}

}

class SBI extends Bank{

float getRateOfInterest(){return 8.4f;}

}

class ICICI extends Bank{

float getRateOfInterest(){return 7.3f;}

}

class AXIS extends Bank{

float getRateOfInterest(){return 9.7f;}

}

class TestPolymorphism{

public static void main(String args[]){

Bank b;

b=new SBI();

System.out.println(“SBI Rate of Interest: “+b.getRateOfInterest());

b=new ICICI();

System.out.println(“ICICI Rate of Interest: “+b.getRateOfInterest());

b=new AXIS();

System.out.println(“AXIS Rate of Interest: “+b.getRateOfInterest());

}

}

Test it Now

Output:

SBI Rate of Interest: 8.4
ICICI Rate of Interest: 7.3
AXIS Rate of Interest: 9.7

Java Runtime Polymorphism Example: Shape

class Shape{

void draw(){System.out.println(“drawing…”);}

}

class Rectangle extends Shape{

void draw(){System.out.println(“drawing rectangle…”);}

}

class Circle extends Shape{

void draw(){System.out.println(“drawing circle…”);}

}

class Triangle extends Shape{

void draw(){System.out.println(“drawing triangle…”);}

}

class TestPolymorphism2{

public static void main(String args[]){

Shape s;

s=new Rectangle();

s.draw();

s=new Circle();

s.draw();

s=new Triangle();

s.draw();

}

}

Test it Now

Output:

drawing rectangle...
drawing circle...
drawing triangle...

Java Runtime Polymorphism Example: Animal

class Animal{

void eat(){System.out.println(“eating…”);}

}

class Dog extends Animal{

void eat(){System.out.println(“eating bread…”);}

}

class Cat extends Animal{

void eat(){System.out.println(“eating rat…”);}

}

class Lion extends Animal{

void eat(){System.out.println(“eating meat…”);}

}

class TestPolymorphism3{

public static void main(String[] args){

Animal a;

a=new Dog();

a.eat();

a=new Cat();

a.eat();

a=new Lion();

a.eat();

}}

Test it Now

Output:

eating bread...
eating rat...
eating meat...

Java Runtime Polymorphism with Data Member

Method is overridden not the datamembers, so runtime polymorphism can’t be achieved by data members.
In the example given below, both the classes have a datamember speedlimit, we are accessing the datamember by the reference variable of Parent class which refers to the subclass object. Since we are accessing the datamember which is not overridden, hence it will access the datamember of Parent class always.

Rule: Runtime polymorphism can’t be achieved by data members.

class Bike{

int speedlimit=90;

}

class Honda3 extends Bike{

int speedlimit=150;

 

public static void main(String args[]){

Bike obj=new Honda3();

System.out.println(obj.speedlimit);//90

}

Test it Now

Output:

90

Java Runtime Polymorphism with Multilevel Inheritance

Let’s see the simple example of Runtime Polymorphism with multilevel inheritance.

class Animal{

void eat(){System.out.println(“eating”);}

}

class Dog extends Animal{

void eat(){System.out.println(“eating fruits”);}

}

class BabyDog extends Dog{

void eat(){System.out.println(“drinking milk”);}

public static void main(String args[]){

Animal a1,a2,a3;

a1=new Animal();

a2=new Dog();

a3=new BabyDog();

a1.eat();

a2.eat();

a3.eat();

}

}

Test it Now

Output:

eating
eating fruits
drinking Milk

Try for Output

class Animal{

void eat(){System.out.println(“animal is eating…”);}

}

class Dog extends Animal{

void eat(){System.out.println(“dog is eating…”);}

}

class BabyDog1 extends Dog{

public static void main(String args[]){

Animal a=new BabyDog1();

a.eat();

}}

Test it Now

Output:

Dog is eating

Since, BabyDog is not overriding the eat() method, so eat() method of Dog class is invoked.

জাভা ইনহেরিটেন্স ( Java Inheritance )

ইনহেরিটেন্স নিয়ে কথা বলতে হলে এর সাথে আরেকটি বিষয় চলে আসে সেটি হলো অবজেক্ট কম্পোজিশান। এটি মোটামুটিভাবে একটু কঠিন অন্যান্য টপিক থেকে। তাই এই টপিকটি পড়ার সময় একটু ধৈর্য্য নিয়ে পড়তে হবে।

তো শুরু করার যাক-

প্রথমেই আমরা কথা বলবো Is – A এবং Has – A নিয়ে।

যেহেতু আমরা জাভা প্রোগ্রামিং শুরু করেছি, তো আমরা যতই এর ভেতরে প্রবেশ করতে শুরু করবো, ততই বুঝতে শুরু করবো যে ক্লাস আসলে একটা স্ট্যান্ড এলোন কম্পোনেন্ট নয়, বরং এটি অন্যান্য ক্লাসের উপর নির্ভর করে। অর্থাৎ ক্লাস গুলো একটি রিলেশন মেইনটেইন করে চলে। এই রিলেশন গুলো সাধারণত দুই ধরণের হয়- Is – A এবং Has – A।

আমাদের বাস্তব জগৎ থেকে একটা এনালজি দেয়া যাক । যেমন একটি বিড়াল, কিংবা কার অথবা বাস। বিড়াল হচ্ছে একটি প্রাণি। কার এর থাকে চাকা এবং ইঞ্জিন। বাস এরও থাকে চাকা এব ইঞ্জিন। আবার কার এবং বাস দুটিই ভেহিকল বা যান।

এখানে যে উদাহরণ গুলো দেয়া হয়েছে এর সবগুলো মূলত Is – A অথবা Has – A রিলেশনশিপ মেইনটেইন করে । যেমন –

A cat is an Animal (বিড়াল একটি প্রাণি ।) A car has wheels ( কার এর চাকা আছে ।)
A car has an engine ( কার এর একটি ইঞ্জিন আছে ।)

তো ব্যপারটি একদম সহজ। ঠিক এই ব্যপারটিকে আমরা আমাদের অবজেক্ট ওরিয়েন্টেট কনসেপ্ট এর মাধ্যমে বলতে পারি। যখন কোন অবজেক্ট এর মাঝে Is – A এই সম্পর্কটি দেখবো তাকে বলবো ইনহেরিটেন্স। আবার যখন কোন অবজেক্ট এর মাঝে Has – A এই সম্পর্কটি দেখবো তখন সেই ব্যপারটিকে বলবো অবজেক্ট কম্পোজিশান।

ইনহেরিটেন্স মূলত একটি ট্রি-রিলেশানশিপ। অর্থাৎ এটি একটি অবজেক্ট থেকে ইনহেরিট করে আসে।

আর যখন আমরা অনেকগুলো অবজেক্ট নিয়ে আরেকটি অবজেক্ট তৈরি করবো তখন সেই নতুন অবজেক্ট হলো মেইড-আপ বা নতুন তৈরি করা অবজেক্ট এই ঘটনাটি হলো কম্পোজিশান।

এর সবই আসলে একটি কনসেপ্ট এবং আইডিয়া থেকে এসেছে, সেটি হলো কোড রিইউজ করা এবং সিম্পল করা। যেমন দুটি অবজেক্ট এর কোড এর কিছু অংশ যদি কমন থাকে তাহলে আমরা সেই অংশটিকে দুইটি ক্লাসের মধ্যে পুনরায় না লিখে বরং তাকে ব্যবহার করতে পারি।

ধরা যাক, আমরা দুটি অবজেক্ট তৈরি করতে চাই- Animal এবং Cat

আমরা জানি যে সব Animal খায়, ঘুমায়। সুতরাং আমরা এই ক্লাসে এই দুটি বৈশিষ্ট্য আমরা এই ক্লাসে লিখতে পারি। আবার যেহেতু আমরা জানি যে Cat হচ্ছে একটি Animal। সুতরাং আমরা যদি এমন ভাবে কোড লিখতে পারি, যাতে করে এই Cat ক্লাসের মধ্যে নতুন করে আর সেই দু্টি বৈশিষ্ট্যের কোড আর লিখতে হচ্ছে না, বরং আমর এই Animal ক্লাসটিকে রিইউজ করলাম, তাহলে যে ঘটনাটি ঘটবে তাকেই মূলত ইনহেরিটেন্স বলা হয়।

এইভাবে আমরা আরও অন্যান্য Animal যেমন, Dog, Cow ইত্যাদি ক্লাস লিখতে পারি।

কম্পোজিশান তুলনামূলক ভাবে একটু সহজ।

Why use inheritance in java

  • For Method Overriding (so runtime polymorphism can be achieved).
  • For Code Reusability.

Syntax of Java Inheritance

  1. class Subclass-name extends Superclass-name
  2. {
  3.    //methods and fields
  4. }

The extends keyword indicates that you are making a new class that derives from an existing class. The meaning of “extends” is to increase the functionality.

In the terminology of Java, a class which is inherited is called parent or super class and the new class is called child or subclass.

Types of inheritance in java

On the basis of class, there can be three types of inheritance in java: single, multilevel and hierarchical.

In java programming, multiple and hybrid inheritance is supported through interface only. We will learn about interfaces later.

types of inheritance in java

Note: Multiple inheritance is not supported in java through class.

When a class extends multiple classes i.e. known as multiple inheritance. For Example:

Why multiple inheritance is not supported in java?

To reduce the complexity and simplify the language, multiple inheritance is not supported in java.

Consider a scenario where A, B and C are three classes. The C class inherits A and B classes. If A and B classes have same method and you call it from child class object, there will be ambiguity to call method of A or B class.

Since compile time errors are better than runtime errors, java renders compile time error if you inherit 2 classes. So whether you have same method or different, there will be compile time error now.

class A{

void msg(){System.out.println(“Hello”);}

}

class B{

void msg(){System.out.println(“Welcome”);}

}

class C extends A,B{//suppose if it were

 

Public Static void main(String args[]){

C obj=new C();

obj.msg();//Now which msg() method would be invoked?

}

}

Test it Now

 Compile Time Error

যেমন আমরা একটি Car তৈরি করতে চাই। Car তৈরি করতে হলে আমাদের লাগবে Wheel এবং Engine. সুতরাং আমরা Wheel এবং Engine এই দুটি ক্লাসকে নিয়ে নতুন আরেকটি ক্লাস লিখবো।

এবার তাহলে একটি উদাহরণ দেখা যাক।

public class Bicycle {

        // the Bicycle class has three fields
        public int cadence;
        public int gear;
        public int speed;

        // the Bicycle class has one constructor
        public Bicycle(int startCadence, int startSpeed, int startGear) {
            gear = startGear;
            cadence = startCadence;
            speed = startSpeed;
        }

        // the Bicycle class has four methods
        public void setCadence(int newValue) {
            cadence = newValue;
        }

        public void setGear(int newValue) {
            gear = newValue;
        }

        public void applyBrake(int decrement) {
            speed -= decrement;
        }

        public void speedUp(int increment) {
            speed += increment;
        }   
    }

উপরের Bicycle ক্লাসটিতে তিনটি ফিল্ড এবং চারটি মেথড আছে। এবার এই Bicycle থেকে আমরা এর একটি সাব-ক্লাস লিখবো

public class MountainBike extends Bicycle {

        // the MountainBike subclass adds one field
        public int seatHeight;

        // the MountainBike subclass has one constructor
        public MountainBike(int startHeight,
                            int startCadence,
                            int startSpeed,
                            int startGear) {
            super(startCadence, startSpeed, startGear);
            seatHeight = startHeight;
        }   

        // the MountainBike subclass adds one method
        public void setHeight(int newValue) {
            seatHeight = newValue;
        }   
    }

এই MountainBike ক্লাসটি উপরে Bicycle এর সব ফিল্ড এবং মেথড গুলো ইনহেরিট করে এবং এতে নতুন করে শুধু একটি ফিল্ড এবং একটি মেথড লেখা হয়েছে। তাহলে আমাদের MountainBike ক্লাসটিতে Bicycle ক্লাসটির সব প্রোপ্রাটি এবং মেথড অটোম্যাটিক্যালি পেয়ে গেলো।

এখানে এ Bicycle হচ্ছে সুপার ক্লাস(Super Class) এবং MountainBike হচ্ছে সাব-ক্লাস(Sub Class) । অর্থাৎ যে ক্লাস থেকে ইনহেরিট করা হয় তাকে বলা হয় সুপার ক্লাস এবং যে ক্লাস সাব ক্লাস থেকে ইনহেরিট করে

Single Inheritance Example

class Animal{

void eat(){System.out.println(“eating…”);}

}

class Dog extends Animal{

void bark(){System.out.println(“barking…”);}

}

class TestInheritance{

public static void main(String args[]){

Dog d=new Dog();

d.bark();

d.eat();

}}

Output:

barking...
eating..

Multilevel Inheritance Example

class Animal{

void eat(){System.out.println(“eating…”);}

}

class Dog extends Animal{

void bark(){System.out.println(“barking…”);}

}

class BabyDog extends Dog{

void weep(){System.out.println(“weeping…”);}

}

class TestInheritance2{

public static void main(String args[]){

BabyDog d=new BabyDog();

d.weep();

d.bark();

d.eat();

}}

Output:

weeping...
barking...
eating...

Hierarchical Inheritance Example

class Animal{

void eat(){System.out.println(“eating…”);}

}

class Dog extends Animal{

void bark(){System.out.println(“barking…”);}

}

class Cat extends Animal{

void meow(){System.out.println(“meowing…”);}

}

class TestInheritance3{

public static void main(String args[]){

Cat c=new Cat();

c.meow();

c.eat();

//c.bark();//C.T.Error

}}

Output:

meowing...
eating...

JVM মেমোরি স্ট্রাকচার (JVM Memory Structure)

JVM (Java Virtual Machine):

JVM (Java Virtual Machine) একটি abstract মেশিন। এটি একটি স্পেসিফিকেশন যা রানটাইম এনভায়রনমেন্ট সরবরাহ করে যার মধ্যে Java বাইট কোডে চালানো যায় ।

JVMs অনেক হার্ডওয়্যার এবং সফ্টওয়্যারের জন্য  available (i.e. JVM প্লাটর্ফম নির্ভরশীল)।

JVM কি ঃ

Continue reading “JVM মেমোরি স্ট্রাকচার (JVM Memory Structure)”

জাভা কন্সট্রাক্টর ( Java Constructor)

কন্সট্রাক্টর হল কোন ক্লাসের মেম্বার ভেরিয়েবল গুলোর মান বসানোর জন্য ব্যবহ্রত একটি মেথড । সাধারনত এর সাহায্যে মেম্বার ভেরিয়েবল এর মান বসানো হয়। আমরা যখন কোন ক্লাসের অব্জেক্ট তৈরি করি তখন আর কোন মেথড কল হয় না,কোন ভেরিয়েবল এ মান বসানো হয়না,শুধু কন্সট্রাক্টর কল হয় তাই এই কন্সট্রাক্টর এর মধ্যে ভেরিয়েবল গুলোর মান বসানো হয়। Continue reading “জাভা কন্সট্রাক্টর ( Java Constructor)”

জাভা ক্লাস(Class) এবং অবজেক্ট (Object)

জাভা ক্লাস :

ক্লাস হলো  অবজেক্ট তৈরি করার প্রক্রিয়ার একটি অংশ। মনে করি আমরা একটি কলম বানাতে চাই, শুরুতে আমরা কোন রকম চিন্তা ভাবনা না করে ফু দিয়ে একটা কিছু বানিয়ে ফেলতে পারি না। আমরা এর জন্যে পরিকল্পনা করি- কলমাটা দেখতে কেমন হবে, এটি লম্বা কতটুকু হবে, কলমটি কি কি কাজ করবে ইত্যাদি। এই পরিকল্পনা গুলো আমরা আমরা কোথাও লিখে রাখি। আমাদের এই লেখা ডকুমেন্টটি আসলে ক্লাস। সহজ একটি ব্যাপার। অবজেক্টকে রিপ্রেজেন্ট করার সহজ উপায় । Continue reading “জাভা ক্লাস(Class) এবং অবজেক্ট (Object)”

জাভা প্রোগ্রামিং নিয়ে আমার লেখা সকল পোস্টের লিঙ্ক

  1. অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং-১

    1. OOPs Concepts
    2. Class and Object
    3. JVM Memory Structure
    4. Working with Buffered Reader Class
    5. Methods
    6. Constructors
    7. Difference Between Method and Constructor
    8. this Keyword in Java
    9. Understanding Encapsulation(Setters and Getters)
    10. static Keyword and its Uses
  2. অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং-২

    1. Inheritance and Inheritance Hierarchy
    2. Types of Inheritance
    3. super Keyword in Inheritance
    4. Polymorphism
    5. Method Overloading and Overriding
    6. Overloading vs. Overriding
    7. Packages
    8. Access Specifiers
  3. Abstract & Interface
    1. Abstraction and Using Abstract Class
    2. Multiple Inheritance in Java
    3. Interface
    4. Abstract classes vs. Interfaces
  4. Arrays,Exception Handling
    1. Introduction to Arrays
    2. One Dimensional Array
    3. Two Dimensional Array
    4. Introduction to Exception Handling
    5. Exception Handling Hierarchy in Java
    6. Errors
    7. Run time exceptions
    8. Compile time Exceptions
    9. try-catch-finally Clause
    10. Cascading Exceptions
    11. throws and throw Keyword
    12. Important points
    13. Custom Exception with Example
  5. String Manipulation
    1. String
    2. StringBuffer
    3. StringBuilder
  6. Threads,Annotations
    1. Introduction to Threads
    2. Need of Threads
    3. Multithreading
    4. Thread Scheduling
    5. Multitasking vs.
    6. Multithreading
    7. Multithreading in context with Java
    8. Thread Creation
    9. Thread Methods
    10. Thread Synchronization and its Importance
    11. Synchronized Methods
    12. Synchronized
    13. Blocks
    14. Deadlock
    15. Producer Consumer Problem
  7. Collections Framework (Data Structures) & Generics
    1. Introduction to Collection Framework
    2. Hierarchy of Collection Framework
    3. Methods of Collection interface
    4. Iterator interface
    5. List interface
    6. ArrayList class
    7. ArrayList vs.Vector
    8. Set and SortedSet interface
    9. Queue
    10. Map
    11. Sorting using Comparator
  8. Generics
    1. Generic & Parameterized Types
    2. Replacing Arrays with ArrayLists
    3. Bounded Type Parameter
    4. Unbounded Wildcard
    5. Generic Methods
    6. Generic Methods & Generic Constructors
  9. Nested Classes
    1. Introduction
    2. Nonstatic Member Classes + Demo
    3. Anonymous Classes + Demo & Item 21 ~ About Function objects
    4. Local Classes + Demo
    5. Static Member Classes + Demo + Item 22: Favor Static Member Classes
      Anonymous Class & Static Member Class Exercise
  10. Concurrency (Multi-threading)
    1. Introduction ~ Motivation & Agenda
    2. Threads: Introduction
    3. Demo: Launching Threads and Making them to Sleep!
    4. Demo: Thread Naming, Prioritization and Joining + Item 73 (Avoid thread groups)
    5. Race Condition + Demo
    6. Synchronization + Demo + Item 66: Synchronize access to shared mutable data
    7. Java Memory Model (JMM)
    8. Volatile Variables + Demo
    9. Atomic Variables + Demo
    10. Synchronization Best Practices (+ Item 67: Avoid excessive synchronization)
    11. Thread Cooperation & Management + Demo
    12. Demo: wait & notify
    13. Thread Interruption + Demo
    14. Explicit Locks (Reentrant Lock) + Demo
    15. Executor Service & Future
    16. Demo: ExecutorService & Future
    17. Tasks with Time Limits + Demo
  11. Design Patterns
    1. স্ট্রাটিজি প্যাটার্ন – Strategy Pattern
    2. অবজার্ভার প্যাটার্ন – Observer Pattern
    3. ডেকোরেটর প্যাটার্ন – Decorator Pattern
    4. সিঙ্গেলটন প্যাটার্ন – Singleton Pattern
    5. স্টেট প্যাটার্ন – State Pattern
    6. কালেকশন প্যাটার্ন – Collection Pattern
    7. কম্পোজিট প্যাটার্ন – Composite Pattern

Continue reading “জাভা প্রোগ্রামিং নিয়ে আমার লেখা সকল পোস্টের লিঙ্ক”