Saturday, 30 May 2015

Java Down-Casting and Up-Casting

A reference to a sub-class object can be stored into a reference type variable of base-class/super-class without any explicit conversion, this is called up-casting. This is like widening conversion used in case of primitive types and takes place implicitly.

A reference to a base-class/super-class object can not be stored into a reference type variable of sub-class without explicit conversion, this is called down-casting. This is like narrowing conversion used in case of primitive types and needs explicit type-casting. The down-casting is valid only if the super class reference points to the sub-class object before down-casting.

Super class variable can refer to a sub-class object

Example: The following example illustrates that a super-class variable can refer to a sub-class object.

Reference variable of a super class can be assigned a reference to any subclass derived from that super class. It is important to understand that it is the type of the reference variable not the type of the object that it refers to that determine what can be accessed.

That is, when a reference to a subclass object is assigned to a super class reference variable, you will have access only to those parts of the object defined by the super class. This is why Box reference cannot access weight even when it refers to a BoxWeight object. If you think about it, this makes sense, because the super class has no knowledge of what a subclass adds to it. This is why the last line of following code is commented out. It is not possible for a Box reference to access the weight field, because it does not define one.

1 class Box

2 { double width, height, depth;

3 double volume()

4 { double vol = width * height * depth;

5 return vol;

6 }

7 Box(double w, double h, double d)

8 { width = w;

9 height = h;

10 depth = d;

11 }

12 Box()

13 { width = -1;

14 height = -1;

15 depth = -1;

16 }

17 Box(double len)

18 { width = height = depth = len;

19 }

20 Box(Box ob)

21 { width = ob.width;

22 height = ob.height;

23 depth = ob.depth;

24 }

25 }

1 class BoxWeight extends Box

2 {

3 double weight;

4 BoxWeight(double w, double h, double d, double m)

5 { width = w;

6 height = h;

7 depth = d;

8 weight = m;

9 }

10 }

1 class BoxWeightDemo

2 { public static void main(String args[])

3   { BoxWeight bw1 = new BoxWeight(10,20,15,34.3);

4    BoxWeight bw2 = new BoxWeight(2,3,4,51);

5 double vol;

6 vol = bw1.volume();

7 System.out.println(vol);

8 System.out.println(bw1.weight);

9 vol = bw2.volume();

10 System.out.println(vol);

11 System.out.println(bw2.weight);

12 Box b1 = new Box();

13 vol = b1.volume();

14 System.out.println(vol);

15 b1 = bw1;

16 vol = b1.volume();

17 System.out.println(vol);

18 //System.out.println(b1.weight); will not compile

19 }

20 }

Output:

3000.0
34.3
24.0
51.0
-1.0
3000.0

Here BoxWeight inherits all of the characteristics of Box and adds the weight component. A major advantage of inheritance is that once you have created a super class that defines the
attributes common to a set of objects, it can be used to create any number of more specific
subclasses. Each subclass can precisely tailor its own classification.

Remember once you have created a super class that defines the general aspects of an object, that super class can be inherited to form specialized classes. Each subclass simply adds its own, unique attributes.

Java Object Class

Even if you write a simple class (i.e. it is not extending any base-class), it implicitly extends built-in Object class. So the Object class is at the root of all the hierarchies in Java. Thus the features of this class will be available in all the classes.

Example: The following example illustrates the basic concepts of inheritance.

class A

{ int i,j; // or protected int i,j; or public int i,j;

void showij()

{ System.out.println(i); System.out.println(j);

}

}

class B extends A

{ int k;

void showk()

{ System.out.println(k);

}

void sum()

{ System.out.println(i+j+k);

}

}

class InheritanceDemo

{ public static void main(String args[])

{ A a = new A();

B b = new B();

a.i = 10; a.j = 20;

a.showij();

b.i = 7; b.j = 8; b.k = 9;

b.showij(); b.showk();

a.showij();

b.sum();

}

}

Output:

10
20
7
8
9
10
20
24

Example: The following example illustrates that although private members are not inherited but
they can be accessed through methods.

1 class A

2 { private int i,j;

3 void showij()

4 { System.out.println(i); System.out.println(j);

5 }

6 void setI(int x)

7 { i = x;

8 }

9 void setJ(int x)

10 { j = x;

11 }

12 int getI()

13 { return i;

14 }

15 int getJ()

16 { return j;

17 }

18 }

19 class B extends A

20 { int k;

21 void showk()

22 { System.out.println(k);

23 }

24 void sum()

25 { System.out.println(getI()+getJ()+k);

26 }

27 }

28 class InheritanceDemo1

29 { public static void main(String args[])

30 { A a = new A();

31 B b = new B();

32 a.setI(10); a.setJ(20);

33 a.showij();

34 b.setI(7); b.setJ(8); b.k = 9;

35 b.showij(); b.showk();

36 a.showij();

37 b.sum();

38 }

39 }

Output:

10
20
7
8
9
10
20
24

Example: The following example defines class Stack with two operations push() and pop(). We
will then extend this class to provide some more features (like adding operations display(), isStackEmpty() and isStackFull().

1 class Stack

2 {

3 int top = -1;

4 int a[] = new int[10];

5 void push(int x)

6 {

7 if(top == 9)

8 { System.out.println("Stack Full");

9 return;

10 }

11 top = top + 1;

12 a[top] = x;

13 }

14 int pop()

15 {

16 if(top ==  -1)

17 { System.out.println("Stack Empty");

18 return -1; //assuming that –1 is not a valid data

19 }

20 int x = a[top];

21 top = top - 1;

22 return x;

23 }

24 }

The following class makes use of the Stack class defined above. In the main method, we have created an instance of the class Stack, pushed 10 elements on the stack and then finally we are removing the elements from the stack using pop() and displaying them. Accepting input from the user to decide what operation is to be performed can further extend the program.

1 class StackDemo

2 { public static void main(String args[])

3 { Stack s = new Stack();

4 for(int i = 0; i < 10; i++)

5 { s.push(100+i);

6 }

7 int x;

8 for(int i = 0; i < 10; i++)

9 {

10 x = s.pop();

11 System.out.println(x);

12 }

13 }

14 }

The following class adds new features to the class Stack by extending it. The methods push() and pop() and instance variables top and a[] are available in the extended class because of inheritance.

1 class ExtStack extends Stack

2 { void display()

3 { for(int i = top; i >= 0; i--)

4 { System.out.println(a[i]);

5 }

6 }

7 boolean isStackEmpty()

8 { if(top == -1)

9 return(true);

10 else

11 return(false);

12 }

13 boolean isStackFull()

14 { if(top == 9)

15 return(true);

16 else

17 return(false);

18 }

19 }

The following class makes use of the ExtStack class, which is derived by extending the class Stack. You can see that it uses the features of the base-class as well as derived class.

1 class StackDemo1

2 { public static void main(String args[])

3 { ExtStack s = new ExtStack();

4 int i=0;

5 for(i = 0; i < 10; i++)

6 { s.push(100+i);

7 }

8 s.push(i); //will lead to stack overflow

9 if(s.isStackFull())

10 System.out.println("Stack is full");

11 s.display(); //Stack will be displayed without deleting

12 int x; //elements are popped() and  displayed

13 for(i = 0; i < 10; i++)

14 { x = s.pop();  System.out.println(x);

15 }

16 s.pop(); //will lead to stack underflow

17 if(s.isStackEmpty())

18 System.out.println("Stack is empty");

19 }

20 }

Java Extending a Class

To inherit a class, you simply incorporate the definition of one class into another by using the extends keyword.

Java supports only Single-Inheritance in the case of classes to avoid ambiguity and complexity although it supports Multiple-Inheritance in the case of interfaces.

The general form for deriving a sub-class from an existing class is as follows:

class <sub-class> extends <base-class>
{

<class-definition>

}

The derived class will have all the features of base-class in addition to the new features defined in the extended class. Java does not support multiple-inheritance so sub-class can extend only one base-class. It is possible that two different classes can extend the same base-class. A sub-class can also be further extended and the hierarchy can go to any depth. Thus by extending a base-class we can form hierarchy of classes all derived from the same base class.

Java Inheritance

Inheritance is a mechanism to inherit the properties of a class in another class. In the terminology of java, a class that is inherited is called a super-class or base-class or parent class. The class that does the inheriting is called a sub-class or child-class or derived class.

Inheritance allows us to extend an existing class (base class) by adding additional features to it as discussed earlier. It encourages the code reusability, which helps in reducing the code size although it may lead to complexity in some cases.

If a built-in class provides most of the needed functionality except one or two features then we
have two alternatives: (i) rewriting the class, and (ii) extending the class.  In most of the cases, second alternative will be a better choice, as we have to write only small piece of code. At the same time we won’t have to spend much time in debugging and testing as the base class is already tested and is in use for a long period of time.

Moreover it is possible that we do not have access to the source code of an existing class as someone else has developed it and what we have is only the class file. So the only way to add new feature is by extending the class to derive a new class with added features. We would not like to modify the existing class even if the source code is available as it might be in use in many other classes and changes to it may affect the other classes using it.

Java Method Returning Boolean Value

The Java supports primitive data type boolean, which can be used as return type.

Example: The following program uses a method that returns boolean value true or false depending on whether a given positive integer number greater than 2 is prime or not.

1 class CheckPrime

2 { static boolean checkPrime(int number)

3 { int root = (int) Math.sqrt(number);

4 for(int i  = 2; i <= root; i++)

5 { if( number % i = = 0)

6 return false; 

7 }

8 return true;

9 } 

10 public static void main(String a[])

11 { int n = Integer.parseInt(a[0]);

12 boolean prime = checkPrime(n);

13 if(prime)

14 System.out.println("The number " + n + " is prime");

15 else

16 System.out.println("The number " + n + " is not prime");

17 }

18 }

13. Examples

Example: The following example shows the static implementation (using array) of stack.

1 class Stack

2 { private int top = -1;

3 private int a[] = new int[10];

4 void push(int x)

5 { if(top == 9)

6 { System.out.println("Stack Full");

7 return;

8 }

9 top = top + 1;

10 a[top] = x;

11 }

12 int pop()

13 { if(top ==  -1)

14 { System.out.println("Stack Empty");

15 return -1; //assuming that -1 is not a valid data

16 }

17 int x = a[top]; top = top - 1;

18 return x;

19 }

20 }

class StackDemo

1 { public static void main(String args[])

2 { Stack s1 = new Stack();

3 Stack s2 = new Stack();

4 for(int i = 0; i < 10; i++)

5 { s1.push(i);

6 }

7 for(int i = 10; i < 20; i++)

8 { s2.push(i);

9 }

10 int x;

11 for(int i = 0; i < 10; i++)

12 { x = s1.pop();

13 System.out.print(x + " ");

14 }

15 System.out.println();

16 for(int i = 0; i < 10; i++)

17 { x = s2.pop();

18 System.out.print(x + " ");

19 }

20 }

Output:

9 8 7 6 5 4 3 2 1 0

19 18 17 16 15 14 13 12 11 10

Example: The following example demonstrates that only one copy per class is created in case of static members. Thus the implementation is not correct if data member top and a[] are declared to be static as in that case all the objects of Stack class will share the same memory.

1 class Stack

2 { static int top = -1;

3 static int a[] = new int[10];

4 static void push(int x)

5 { if(top == 9)

6 { System.out.println("Stack Full"); 

7 return;

8 }

9 top = top + 1;

10 a[top] = x;

11 }

12 static int pop()

13 { if(top ==  -1)

14 { System.out.println("Stack Empty");

15 return -1; //assuming that -1 is not a valid data

16 }

17 int x = a[top];

18 top = top - 1;

19 return x;

20 }

21 }

1 class StackDemo

2 { public static void main(String args[])

3 { Stack s1 = new Stack();

4 Stack s2 = new Stack();

5 for(int i = 0; i < 10; i++)

6 { s1.push(i);

7 }

8 for(int i = 10; i < 20; i++)

9 { s2.push(i);

10 }

11 int x;

12 for(int i = 0; i < 10; i++)

13 { x = s1.pop();

14 System.out.print(x + " ");

15 }

16 System.out.println();

17 for(int i = 0; i < 10; i++)

18 { x = s2.pop();

19 System.out.print(x + " ");

20 }

21 }

22 }

Output:

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

Stack Full

9 8 7 6 5 4 3 2 1 0

Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1 Stack Empty

-1

Example: The following example shows the dynamic implementation (using linked list) of stack.

1 class Node

2 { Node nextNode;

3 int x;

4 }

1 class Stack

2 { Node top;

3 void push(int item)

4 { Node temp;

5 if(top == null)

6 { top = new Node();

7 top.x = item; top.nextNode = null;

8 }

9 else

10 { temp = new Node();

11 temp.x = item;

12 temp.nextNode = top;

13 top = temp;

14 }

15 }

16 int pop()

17 { if(top == null)

18 { System.out.println("Stack is empty");

19 return(-1);

20 }

21 else

22 { int x = top.x; top = top.nextNode;

23 return x;

24 }

25 }

26 } 

1 class StackTest

2 { static public void main(String args[])

3 { Stack st = new Stack();

4 st.pop();

5 for(int i = 0; i < 5; i++)

6 st.push(i); 

7 for(int i = 0; i <= 5; i++)

8 System.out.println(st.pop()); 

9 }

10 }

Output:

Stack is empty

4

3

2

1

0

Stack is empty

-1