Showing posts with label Polymorphism. Show all posts
Showing posts with label Polymorphism. Show all posts

Saturday, May 7, 2011

Abstraction v/s Encapsulation

Java is an Object Oriented Programming language. Each object oriented programming language is standing on the following pillars.

A. Polymorphism
B. Abstraction
C. Inheritance
D. Encapsulation

Now everyone who deals with Java code, surely understands inheritance and they have  a fair idea about polymorphism. Everyone knows inheritance because Java has forced developers to use inheritance. Each class inherits Object class internally. That is the reason we all understand Inheritance.

Polymorphism, is little more complex and less used concept. There are possibilities that developer may code thousand lines without encountering necessity of polymorphism in his code.

Abstraction and encapsulation are least used principle in Java programming language. Usually a beginner could not understand the analogy behind abstraction whereas  a beginner uses encapsulation logic by default. So we never try to explore these concepts until one fine day an interviewer asks the difference between Abstraction and Encapsulation. Seriously we should take interviews to increase our knowledge about technology.

If we need to define these principle's in two words then we can say Abstraction is implementation hiding where as encapsulation is Data Hiding.

What is Abstraction?
Class's definition is not complete and undefined definition is completed by child classes.
Weird, is not it? When there is no scope of creating a class then why having such class?

Database drivers are best example to illustrate abstraction. JDBC has defined a set of abstract interfaces/ abstract classes. Database providers have to complete the implementation for the following interfaces and classes. Now to interact with one database, developer need to register respective driver without changing the code. Developer does not need to mug up multiple databases to code for each of them. So using abstraction, problem of tackling different databases has been handled brilliantly. So without disclosing the actual implementation, desired functionality has been achieved.

What is Encapsulation?
Data is not directly accessible to user. It is much safer and recommended to make all fields as private and provide field accessors ( getter and setter methods ). Private is used so that no one could destroy the data intentionally and unintentionally and accessors methods provide more flexibility and control over the field.

Let's try to understand this with Walmart stores located in different countries. Let's say Walmart is trying to sell one product across countries. So for each country it will be inappropriate to have different instance of same product. To save the price of product they need to convert the price while storing and displaying, so having getter and setter methods give the control on data.

Here is the little difference between the use of abstraction and encapsulation. Hope I am able to clarify a little about complex and important concept of core Java.

Sunday, October 24, 2010

Design Pattern-2

As it was not possible to cover all the pattern in the previous post. In this post I am going to summarize the other design patterns.
Structural Patterns
Adapter: This pattern is called Wrapper pattern as well. Using this pattern, one class is made compatible with another class. For example, interface X defines 2 methods get and put. Class LegacyClass has methods implementing the required functionality but as LegacyClass can not implement newly defined interface X. So there is a need to wrap LegacyClass with Interface. Adapter pattern will provide the solution to break the ice.
Bridge: This pattern is to provide encapsulation, inheritance and abstraction to the code. In this pattern one interface is given whose implementation can be provided at lower class hierarchy keeping it hidden from the client.
Composite: Composition of no of objects to form an object. This pattern suggests to use numerous objects to create one object.
Decorator: The decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime, independently of other instances of the same class, provided some groundwork is done at design time. This is achieved by designing a new decorator class that wraps the original class.
Facade: Provide one unified interface to a set of interfaces. This unified interface works at higher level and it makes it easy to use all the interfaces at once.
Flyweight: A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
Private Class Data: This pattern primarily deals with encapsulation to hide the data.
Proxy: A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.

Behavioral patterns
Chain of responsibility pattern: This pattern is a source of command object and series of processing objects. Each processing object contains a set of logic that describes the types of command objects that it can handle, and how to pass off those that it cannot handle to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
Command pattern: In this pattern object stores the information to invoke the method at later point of time. The information stored is method’s name. parameters and object to which method belongs to. There are 3 components of the pattern:
  • Client: Instantiate a command object and provide the information required to call the method at later point of time.
  • Invoker: Class which decides when to call the method.
  • Receiver: Object having method.
Interpreter pattern: Rules to identify the sentence in language. This pattern is generally used when someone needs to implement own parser.
Iterator pattern: There are aggregated data and there is need to access the data sequentially. So with exposing the internal DS, the same problem is solved by iterator patterns. Java collection API uses iterator pattern.
Mediator pattern: It is very similar facade patterns, where unified interface is exposed by extending numerous interfaces in the subsystem. This pattern is used in these circumstances.
  • Partition a system into pieces or small objects.
  • Centralize control to manipulate participating objects(a.k.a colleagues)
  • Clarify the complex relationship by providing a board committee.
  • Limit subclasses.
  • Improve objects re-usabilities.
  • Simplify object protocols.
  • The relationship between the control class and other participating classes is multidirectional.
Memento pattern: This pattern provide the ability to roll back. Here you will keep 2 states of an object one that is original i.e. anything before changes whereas other object will have latest copy with updates. This pattern is utilized in database updates.
Null Object pattern: Pattern just portrait the behavior of nothing.
Observer pattern: In this pattern a object keeps the list of its dependent, called observer. Whenever changes happened in the object its observer are notified by calling some methods. In general this pattern is used in event handling.
Weak reference pattern: A WeakReferencePattern is a structural pattern used when decoupling of an observer (a view) from an observable (a container) is necessary. A WeakReferencePattern encapsulates a reference to an object. Acquiring the reference is done through a message to the WeakReference (or WeakPointer) object. If the referenced object still exists, a real reference to it is returned. This reference is of course a temporary one.
State pattern: As the state of the object changes behavior is changed. Behavior is changes with if and else statements.

Strategy pattern: The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them. It is useful in the following situations
  • Encapsulate various algorithms to do more or less the same thing.
  • Need one of several algorithms dynamically.
  • The algorithms are exchangeable and vary independently
  • Configure a class with one of many related classes (behaviors).
  • Avoid exposing complex and algorithm-specific structures.
  • Data is transparent to the clients.
  • Reduce multiple conditional statements.
  • Provide an alternative to subclassing.
Specification pattern: In this pattern business logic can be recombined by chaining the business logic together using boolean logic.
Template method pattern: Two different components have significant similarities, but demonstrate no reuse of common interface or implementation. If a change common to both components becomes necessary, duplicate effort must be expended. Provide an abstract definition for a method or a class and redefine its behavior later or on the fly without changing its structure.
Visitor pattern: Define a new operation to deal with the classes of the elements without changing their structures. These are used in the following circumstance:
  • Add operations on a bunch of classes which have different interfaces.
  • Traverse the object structure to gather related operations
  • Easy to add new operations.
  • Crossing class hierarchies may break encapsulation.
Apart from above described patterns there are lot more patterns few of them are Single-serving visitor pattern, Hierarchical visitor pattern and Scheduled-task pattern. There are lot other than mentioned here so it is quite impossible to cover all of them. Hope this information will help all of us.


Source:

Thursday, July 1, 2010

Equals and HashCode

In java each class internally extends the Object class, which in turns implies that all the methods which are available to the Object class are also available to all the class in the project.

Object class has numerous methods but among them there are few methods which should be overridden for each class. Among those method 2 methods are equals and hashCode.

Source code for equals method is as shown below:

public boolean equals(Object obj) {
    return (this == obj);
}

According to this, whenever references of parameter object is similar to the reference of this, equals returns true true otherwise false.


Source code for hashCode method is as shown below:



public native int hashCode();

This method returns a integer which is unique to each object and if will return same hashCode is called n number of times on the same object.


Now questions comes why do I need to overrides these methods which is already implemented by Java?


Lets look at the example mentioned below:



class UniqueIdentifier {
    String name = "";
    int id = 0;  
    public UniqueIdentifier(int ids, String n) {
        name = n;
        id = ids;
    }  
}
 
public class UnderstandingEqualsHashCode {        
    public static void main(String[] args){
        UniqueIdentifier person1 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person2 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person3 = person1;
        
        System.out.println("Person1:" + person1.toString());
        System.out.println("Person2:" + person2.toString());
        System.out.println("Person3:" + person3.toString());
        
        System.out.println("Person1 HashCode:" + person1.hashCode());
        System.out.println("Person2 HashCode:" + person2.hashCode());
        System.out.println("Person3 HashCode:" + person3.hashCode());
 
        if(person1.equals(person2)){
            System.out.println("Person1 and person2 are same.");
        }else{
            System.out.println("Person1 and person2 are different.");
        }
        
        if(person1.equals(person3)){
            System.out.println("Person1 and person3 are same.");
        }
        
    }
    
}
If you will execute the above code the output will be:

Person1:UniqueIdentifier@19821f
Person2:UniqueIdentifier@addbf1
Person3:UniqueIdentifier@19821f
Person1 HashCode:1671711
Person2 HashCode:11394033
Person3 HashCode:1671711
Person1 and person2 are different.
Person1 and person3 are same.

Shocked?

First 3 lines shows the results of the toString method, where you can see Class name along with the hexadecimal codes. So here we can see toString returns the class name appended by hashCode in hexadecimal format.

As you can see, for a human person1 and person2 are the equal objects but Java does not considering them same as it has to allocate the different memory and accordingly a different hashCode, whereas person1 and person3 are same for java because person1 and person3 are referring to the same memory location.

Now it must be clear why do we need to implement equals… But if we are implementing equals then due to a contract between hashCode and equals, it becomes programmers responsibility to define the hashCode as well. Though java does not force programmer to implement both.

The general contract between hashCode() and equals() methods is:


  • Whenever hashCode is invoked on the same object more than once during an execution of a Java application, it must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

Look at the example mentioned below



class UniqueIdentifier {
    String name = "";
    int id = 0;
    public UniqueIdentifier(int ids, String n) {
        name = n;
        id = ids;
    }
    public boolean equals(Object obj){
        if( obj instanceof UniqueIdentifier ){
            UniqueIdentifier ui = (UniqueIdentifier)obj;
            if(name.equals(ui.name) && (id == ui.id)){
                return true;
            }
        }
        return false;
    }
}
 
public class UnderstandingEqualsHashCode {
    public static void main(String[] args){
        UniqueIdentifier person1 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person2 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person3 = person1;
        UniqueIdentifier person4 = new UniqueIdentifier(105, "XYZ");
        Object abs = new Object();
        System.out.println("Person1:" + person1.toString());
        System.out.println("Person2:" + person2.toString());
        System.out.println("Person3:" + person3.toString());
        System.out.println("Person1 HashCode:" + person1.hashCode());
        System.out.println("Person2 HashCode:" + person2.hashCode());
        System.out.println("Person3 HashCode:" + person3.hashCode());
        if(person1.equals(person2)){
            System.out.println("Person1 and person2 are same.");
        }else{
            System.out.println("Person1 and person2 are different.");
        }
        if(person1.equals(person4)){
            System.out.println("Person1 and person4 are same.");
        }else{
            System.out.println("Person1 and person4 are different.");
        }
        if(person1.equals(abs)){
            System.out.println("Person1 and abs are same.");
        }else{
            System.out.println("Person1 and abs are different.");
        }
        if(person1.equals(person3)){
            System.out.println("Person1 and person3 are same.");
        }
    }
}


In the above mentioned example equals method has been implemented whereas hashCode is not implemented yet.


There 4 conditions have been tested for equality among



  • Person1 and person2.
  • Person1 and person4
  • Person1 and person3
  • Person1 and abs

Results of the above tests are


Person1:UniqueIdentifier@19821f
Person2:UniqueIdentifier@addbf1
Person3:UniqueIdentifier@19821f
Person1 HashCode:1671711
Person2 HashCode:11394033
Person3 HashCode:1671711
Person1 and person2 are same.
Person1 and person4 are different.
Person1 and abs are different.
Person1 and person3 are same.

Results are little shocking… Is not it? Even though hashCode is not implemented, equality test between person1 and person2 shows different results…

I just want to state equality test does not depend whether hashCode has been implemented or not, but it is good to have it.

Ok, when it does not matter for the equality test then why bother about implementing it?

Have a look on the code mentioned below:


import java.util.HashSet;
import java.util.Set;
 
class UniqueIdentifier {
    String name = "";
    int id = 0;
    public UniqueIdentifier(int ids, String n) {
        name = n;
        id = ids;
    }
    public boolean equals(Object obj){
        if( obj instanceof UniqueIdentifier ){
            UniqueIdentifier ui = (UniqueIdentifier)obj;
            if(name.equals(ui.name) && (id == ui.id)){
                return true;
            }
        }
        return false;
    }
}
 
public class UnderstandingEqualsHashCode {
    public static void main(String[] args){
        UniqueIdentifier person1 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person2 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person3 = person1;
        UniqueIdentifier person4 = new UniqueIdentifier(105, "XYZ");
        UniqueIdentifier person5 = new UniqueIdentifier(105, "XYZ");
        
        System.out.println("Person1 HashCode:" + person1.hashCode());
        System.out.println("Person2 HashCode:" + person2.hashCode());
        System.out.println("Person3 HashCode:" + person3.hashCode());
        System.out.println("Person4 HashCode:" + person4.hashCode());
        System.out.println("Person5 HashCode:" + person5.hashCode());
        
        Set<UniqueIdentifier> personSet = new HashSet<UniqueIdentifier>();
        personSet.add(person1);
        personSet.add(person2);
        personSet.add(person3);
        personSet.add(person4);
        personSet.add(person5);
        personSet.add(person1);
        
        System.out.println("Size:" + personSet.size());
    }
}


Here I have created 5 references of the UniqueIdentifier class and I added all of them in a HashSet. A set can only have unique entries. So uniqueness of the object is defined by equal method. If java finds there is an element equal to object to be added, then it discard the object without adding it, else creates a new entry in the set.


On the basis of logic stated above, it is clear size of the Set should be 2. Now lets look for the output of program listed above.


Person1 HashCode:11394033
Person2 HashCode:4384790
Person3 HashCode:11394033
Person4 HashCode:9634993
Person5 HashCode:1641745
Size:4


How did the count reach to 4, where it is quite obvious it should contain only 2 records? There should be some bug in the code, but it is not. Okay to understand little more lets understand the logic to add object in HashSet class.



HashSet.java
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
 
HashMap.java
public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
 
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}


From the above code it is quite clear, that hashCode is used to identify the bucket of the element and after identification the bucket it tries to iterate through the elements of the existing bucket and try equating them. In last example where hashCode was not implemented, element will fetch object’s hashcode and try to find the bucket if it could not find any it will add the element separately as a different entry.


Have a look on the code mentioned below:



import java.util.HashSet;
import java.util.Set;
 
class UniqueIdentifier {
    String name = "";
    int id = 0;
    public UniqueIdentifier(int ids, String n) {
        name = n;
        id = ids;
    }
    public boolean equals(Object obj){
        if( obj instanceof UniqueIdentifier ){
            UniqueIdentifier ui = (UniqueIdentifier)obj;
            if(name.equals(ui.name) && (id == ui.id)){
                return true;
            }
        }
        return false;
    }
    
    public int hashCode(){
        return (name.hashCode() + id);
    }
    
    public String toString(){
        return("Name:" + name + " Id" + id);
    }
}
 
public class UnderstandingEqualsHashCode {
    public static void main(String[] args){
        UniqueIdentifier person1 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person2 = new UniqueIdentifier(100, "XYZ");
        UniqueIdentifier person3 = person1;
        UniqueIdentifier person4 = new UniqueIdentifier(105, "XYZ");
        UniqueIdentifier person5 = new UniqueIdentifier(105, "XYZ");
        
        System.out.println("Person1 HashCode:" + person1.hashCode());
        System.out.println("Person2 HashCode:" + person2.hashCode());
        System.out.println("Person3 HashCode:" + person3.hashCode());
        System.out.println("Person4 HashCode:" + person4.hashCode());
        System.out.println("Person5 HashCode:" + person5.hashCode());
        
        Set<UniqueIdentifier> personSet = new HashSet<UniqueIdentifier>();
        personSet.add(person1);
        personSet.add(person2);
        personSet.add(person3);
        personSet.add(person4);
        personSet.add(person5);
        personSet.add(person1);
        
        System.out.println("Size:" + personSet.size());
    }
}

Here in this code, i have implemented hashCode, equals and toString (Not necessary) methods. Now if you look at the results you will see the results that is expected while writing the class and adding them into hashSet.


Here is the result of the updated class.

Person1 HashCode:87517
Person2 HashCode:87517
Person3 HashCode:87517
Person4 HashCode:87522
Person5 HashCode:87522
Size:2

Miracle, this is what i expected. So it is quite clear if we want to support equal in a class it makes sense to implement hashCode to achieve the desired results. 

Though it is possible to define a constant hashCode, as it meets with the contract between hashCode and equals, but defining constant hashCode will abolish the purpose of hashing and worsen the performance of the hashing based algorithm. So we need to take care of the equals, hashCode contract and provide the best possible hashing algorithm to make the hash based algorithms more efficient and fast.


Disclaimer: All the material here is the result of research and understanding of other material. If there is some content which is either incorrect or inappropriate to the context of the article I am always open for discussion.