Java OOPs- Inheritance

Inheritance in java is one of the most important concept that is used most extensively in real world applications.

In real world, a web application is made of different layers like front end that is seen by user and on which user interacts ,when user sends requests to server front layer , the one that handles the request and send requests to next layer, business layer i.e layer which handles all the business logic and dao layer i.e database layer come into picture.

Every layer has a base class like for example in case of using Spring framework for mvc mechanism , we define a BaseController class , this class has all the basic methods like getting details of logged in user and storing the info in session and many more methods that can be inherited and used by all the other controller classes. A need for inheritance arises when we need some base class having all the generalized methods and fields which a specialized child class can inherit from base class and then add some more specialized methods and fields.

Inheritance in java means by extending a class , the child class , the one that extends , inherits all the fields and methods of the parent class. Inheritance thus promotes code reusability.

Apart from classes , interfaces can also inherit from their parent interfaces by extending the same.

Lets dive deeper into inheritance

Inheritance with classes

1)In Java, when an “Is-A” relationship exists between two classes we use Inheritance.
2)The parent class is termed super class or base class and the inherited class is the sub class.
3)The keyword “extend” is used by the sub class to inherit the features of super class.

Java only supports single inheritance between classes. This means a class can inherit only from a single class , it cannot extend multiple classes.

A derived class inherits all the public and protected members from the base class, which are not static.In addition, the members with default access are inherited if the child class is in the same package as parent class.

Inheritance and Type Casting

A parent class reference can always refer to and have instance of sub class.
For example, suppose student class extends Parent class.This is referred to as upcasting (from a subclass type to a superclass type).

Student student=new Student();
Person person=student;

The above kind of instances one can find throughout java code as this is also one of the best coding practices . As long as it is possible one should declare variable type as that of Parent class, method arguments should also be declared as parent class variable to make it resuable for different child class.

Example, public void test(List list) , now this list can have instance of either an ArrayList, LinkedList any child class.

It may also be possible to cast an object from a superclass type to a subclass type, but only if the object really is an instance of that subclass (or an instance of a subclass of that subclass). This is referred to as downcasting (from a superclass type to a subclass type).

Student student=new Student();
Person person=student;
Strudent student2=(Student)person;

 

Advertisements

JDK vs JRE

Today one of my juniors had this doubt whether she should install JRE or JDK on her system so I thought to share the difference on the same.

JRE: Java Runtime Environment , as the name suggests it provides required environment with its java libraries and java virtual machine(JVM) to run java programs. If you only need to run java programs then JRE is the only thing that you need on your system.

However, with JRE you can only run compiled java programs, it cannot compile java programs as it doesnt contain java compiler i.e javac to do the same. If you look into the bin folder of a JRE you wont find any javac.

JDK: Java Development Kit, it is a development kit that contains complete JRE alongwith other java development tools like java compiler i.e javac , a documentation generator i.e javadoc , java debugger. Using JDK, you compile .java files to .class files that contain bytecodes understandable by the JVM.

Internal working of HashMap in java

Basically HashMap is an array of Entry class, a static inner class defined in HashMap is as below:

static class Entry implements Map.Entry {
final K key;
V value;
Entry next;
final int hash;

//getters and setters

}

Each element in a Hashmap stores in it a key, its value, reference to next entry in case hashvalue is same for two entries in that case a linked list will be formed.

Default initial capacity of a HashMap is 16 i.e a HashMap is initialized with a default 16 sized array.

Now, lets see what happens when we put an element into HashMap.
Following is the code for put method in hashmap:

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 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;
}

private V putForNullKey(V value) {
for (Entry e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}

Lets decode the above lines of code;
1)As the HashMap allows values to be stored with null keys in the map, firstly we check if key is null or not.
2)If key is null,putForNullKey method is called and null key value is placed at 0th position of array.
for (Entry e = table[0]; e != null; e = e.next) {//As each element with same hashvalue forms linked list in a hashmap array, we iterate the element using a for loop.If e==null, we add the new element.

3)If key is not null, we need to find the index in the array where we will store our element and this is done using hashing.
int hash = hash(key.hashCode());//We calculate a unique hash corresponding to each element.

int i = indexFor(hash, table.length);//Passing the hash and array length we find the position in array to store the element.

static int indexFor(int h, int length) {
return h & (length-1);
}

As you can see we do & operation between array length and hash value and thus a location is found from 1to16, so suppose hash value is 00000000 00010000 & 00000000 00010000, will give an index of 16.

4)Next we check if an element entry exists at above found index if it doesnt exist we add the new element entry but if an entry element already exists at the index position , here comes the linkedlist into the picture.

5)In the if condition if (e.hash == hash && ((k = e.key) == key || key.equals(k))) , we compare the hash value and a comparison with the equals method written for the object or for the same references.

6)If above condition is true, old value is replaced with new value.
7)If 5th condition is false, we iterate the linked list till we find the exact key location and put the entry element.

Implementing two interfaces with same default methods.

In java8 , a new concept is introduced wherein we can provide implementation for methods in an interface using default keyword. But what will happen if a class implements two interfaces with same default methods , which method the implementing class will inherit. Let us see this in the following working example.

I have one interface, TestInterface1 with a default method test()

package com.test;

public interface TestInterface1 {

public default void test() {
System.out.println(“hi”);
}
}

An another interface, TestInterface2 too has implementation for method test()

package com.test;

public interface TestInterface2 {

default public void test() {
System.out.println(“hello”);
}
}

Now, if I have a class Test that implements above two interfaces , the compiler gives me an error “Duplicate default methods named test with the parameters () and () are inherited from the types TestInterface2 and TestInterface1”

package com.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.util.stream.Collectors.*;

import java.lang.reflect.Method;

public class Test implements TestInterface1,TestInterface2{

public static void main(String[] args) {
Test test=new Test();
test.test();

}

However, I can remove the above error by providing the compiler which interface method implementation I need in my implementation class.

package com.test;

public class Test implements TestInterface1,TestInterface2{

public static void main(String[] args) {
Test test=new Test();
test.test();

}

@Override
public void test() {
// TODO Auto-generated method stub
TestInterface1.super.test(); }
}

So, as shown above in my implementation class method I need to call the method with the interface name whose method implementation I need.

Behaviour of static variable in inheritance

Parent and child instances share a same copy of static variable. This I concluded running the following example.

package com.test;

public class Fruit {

static int count=0;

Fruit(){
count++;
}

public static void main(String[] args) {
Chickoo chickoo=new Chickoo();
System.out.println(chickoo.count);
Fruit fruit=new Fruit();
System.out.println(fruit.count);

}
}

class Chickoo extends Fruit{
Chickoo(){
count++;
}
}

The above code prints value of count as “2” and “3”.

Unexpected results for TreeMap with inconsistent compareTO and equals.

If two objects are considered equal by compareTo() and not by equals() or vice-versa, then TreeSet and TreeMap may produce different output.

public class Employee implements Comparable <<>>
{
int empNo;
String empName;

@Override
public int hashCode() { return empNo; }

@Override
public boolean equals(Object obj)
{
Employee other = (Employee) obj;
if (empNo == other.empNo && empName.equals(other.empName))
return true;
return false;
}

@Override
public int compareTo(Employee other)
{
return (empNo – other.empNo);
}

@Override
public String toString() { return “[” + empNo + “, ” + empName + “]”; }

public Employee(int empNo, String empName)
{
this.empNo = empNo;
this.empName = empName;
}

public static void main(String[] args)
{
Employee employees[] = new Employee[5];
employees[0] = new Employee(1, “Grima”);
employees[1] = new Employee(2, “Krishma”);
employees[2] = new Employee(2, “Krishma”);
employees[3] = new Employee(4, “Krishma”);
employees[4] = new Employee(2, “Gurav”);

HashSet set = new HashSet();
set.addAll(Arrays.asList(employees));
System.out.println(set.toString());

TreeSet treeSet = new TreeSet();
treeSet.addAll(Arrays.asList(employees));
System.out.println(treeSet.toString());
}
}

[[1, Grima], [2, Krishma], [2, Gurav], [4, Krishma]] // HashSet
[[1, Grima], [2, Krishma], [4, Krishma]] //TreeSet

Now as per the “equals” method in Employee two employee objects will be equal if their empNo and empName are equal while “compareTo” method says that two employee objects will be equal if two employee objects have same empNo.

So the output of the hashset is expected, employees with different empNo. and empName are added to set, but if we look at output of TreeSet, employees only with different empNo are added.

Thus we can conclude that TreeMap does not use hashCode() or equals() and it uses the compareTo() method.
This could result in a serious application error where two objects considered equal by equals() return different values from TreeMap.

Summary: All 3 methods – compareTo, equals and hashCode should be consistent with each other.

Java 8 streams filter

package com.test;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Customer {

private String firstName;

private String lastName;

private int orderNo;

public Customer(String firstName, String lastName, int orderNo) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.orderNo = orderNo;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getOrderNo() {
return orderNo;
}

public void setOrderNo(int orderNo) {
this.orderNo = orderNo;
}

public static void main(String[] args) {
List customers=new ArrayList();
customers.add(new Customer(“garima”,”pandey”,1));
customers.add(new Customer(“ashu”,”dikshit”,2));
customers.add(new Customer(“gaurav”,”dixit”,3));

//1)Filter and foreach
//The following basically streams the collection and on a stream you can have multiple operations performed
//The stream has three parts, first is the source which in our case is customers collection, second part is all the operations that need to be performed on that stream, so below is a filter operation that is performed on the stream and finally we have a terminal operation that is the end condition, below for each is the end condition

customers.stream().filter(c->c.getLastName().startsWith("d")).forEach(c->System.out.println(c.getFirstName()));// output-->ashu,gaurav

//2)Filter and collect
//stream.filter() lets you to filter a list and collect() to convert stream into a list.

List collected=customers.stream().filter(c->c.getFirstName().startsWith("g")).collect(Collectors.toList());
collected.forEach(c->System.out.println(c.getFirstName()));//output-->garima,gaurav

//3)filter and .findAny().orElse(null)

Customer cust=customers.stream().filter(c->c.getFirstName().contains("garima")).findAny().orElse(null);
System.out.println(cust.getFirstName());//output-->garima

//4)filter and .findAny().orElse(null)

Customer custNull=customers.stream().filter(c->c.getFirstName().contains("test")).findAny().orElse(null);
System.out.println(custNull);//output-->null

//5)filter and map
//map function can be used to perform some operation on all of it’s elements.

customers.stream().filter(c->c.getFirstName().startsWith("g")).map(Customer::getFirstName).collect(Collectors.toList()).forEach(System.out::println);//output->garima,gaurav

}
}