By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,701 Members | 1,955 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

A quick introduction to Java concepts 2

10K+
P: 13,264
Inheritance

We have already covered one important concept of object-oriented programming, namely encapsulation, in the previous article. These articles are not articles on object oriented programming but on Java programming but I will cover all the important aspects of object oriented programming as Java has full support for object-oriented
programming even though it is not a fully object-oriented language (in the sense that you can write non object-oriented programs using Java).
This article focuses on inheritance which is a very important concept in object-oriented programming and is a huge factor in the design of the Java programming language.

I did promise however to look at the String class first so here goes:

Aside: Strings in Java

String literals in Java are instances of the java.lang.String class. A string literal is identifiable by being wrapped in double quotes, "". A String is essentially an array of characters. (will look at arrays later). It is very important to be able to realize that Java strings are constants whose value cannot be changed (immutable) just like the integer literal 3 or any other literal in Java. This means that once created a string remains the same until it is removed from memory. So let's get things really cleared up here. String type differs from byte, int, double, char and long types in that the String type is actually a class whereas all these others are primitive types and are not constructed from any class. However, the string literals themselves are constants and their values do not change. So when you use a string method like concat you do not modify the string that called the method

Expand|Select|Wrap|Line Numbers
  1. String hello = "Hello";
  2. String world = " World";
  3. hello.concat(world);
  4. System.out.println(hello);
Prints Hello to the screen. Calling the method did not change the string literal "Hello" because a literal's value cannot be changed. The call however created a new String and returned it. I just did not make use of it there. To see it we assign it to a string variable e.g
Expand|Select|Wrap|Line Numbers
  1. String hello = "Hello";
  2. String world = " World";
  3. String helloWorld = hello.concat(world);
  4. System.out.println(helloWorld);
You can change the value that a string variable is referencing. Let's go through this:

Expand|Select|Wrap|Line Numbers
  1. String string = "something";
  2.  
The variable string refers to the literal "something". From this moment on "something" will always be "something". No matter what we do to the variable string, the literal does not change. But if we next write:
Expand|Select|Wrap|Line Numbers
  1. string = "nothing";
  2.  
Now string refers to the literal "nothing". We did not change "something" to become "nothing". We just removed the reference from the literal "something", to the literal "nothing". This should be well understood. You may notice that we no longer have anything referring to "something". That is certainly interesting (so interesting in fact that we shall probably devote a whole class to it one day).
When you do
Expand|Select|Wrap|Line Numbers
  1. 1.String s = "hello";
  2. 2.s = s.concat(" world");
you create a variable that references the literal "hello" in line 1, that's fine. In line 2, first you create a string that has the literal referred to by s concatenated with the literal " world", then you change the value referred to by s from "hello" to that newly created
string. No string was expanded. Only a reference was changed from referencing a shorter string to referencing a longer string. Surely you get it now. I have deliberately dwelt a long time on this because once you grasp that, the rest of your string manipulation will be much easier.

Let's cut back to inheritance for now and leave this until the next aside.

Defn: Inheritance

Let's call it the capability of a class or interface (we'll look at interfaces at the end of this article) to use a subset of the properties and methods of another class (or interface) with the added ability of adding its own properties and methods. Some points to remember are in order here:

1.) Inheritance occurs at class or interface level. Classes inherit from other classes and interfaces inherit from other interfaces. No mixing. (We will see that one is actually boss to the other so they really should not mix).

2.) If Class B inherits from class A, it does not mean that all the properties and methods of A are accessible in Class B. It is possible to hide certain properties and methods so that they are not accessible in inheriting classes.

3.) The inheriting class is allowed to add its own properties and methods. This is typically the case otherwise they will be no point in introducing a new class into the program.

4.) Watch out guys, constructors are not inherited!

Here is an example
Expand|Select|Wrap|Line Numbers
  1.  class Animal {
  2. boolean domestic; 
  3.  
  4. public Animal () {
  5. }
  6. public Animal(boolean domestic) {
  7. this.domestic = domestic;
  8. }
  9. public boolean isDomestic() {
  10. return domestic;
  11. }
  12. }
  13. class Cat extends Animal {
  14.  
  15. public Cat(boolean domestic) {
  16. this.domestic = domestic;
  17.  
  18. }
  19. public String toString() {
  20. return "This cat domestic?:"+isDomestic();
  21. }
  22. }
  23. class Dog extends Animal {
  24.  
  25. public Dog(boolean domestic) {
  26. this.domestic = domestic;
  27. }
  28. public String toString() {
  29. return "This dog domestic?:"+isDomestic();
  30. }
  31. }
  32. public class AnimalTest{
  33. public static void main(String[] args) {
  34. System.out.println(new Dog(false).toString());
  35. System.out.println(new Cat(true).toString());
  36.  
  37. }
  38. }
  39.  
  40.  
The keyword extends is used to define the inheritance relationship.
The variable domestic and the method isDomestic are both inherited in Dog and in Cat classes. There is no need for re-declaring them again. Just use them as if you defined them there.

Now don't say the wrong things. The terminology is that the inheriting class is called the subclass and the class inherited from is called the super class. So Dog and Cat are subclasses of Animal in this case.

Every class written in Java except the Object class has exactly one super class. That's how important inheritance is to Java. Every class you are going to write is going to have a super class. All classes inherit from the object class directly if they have no super class or indirectly if they have a super class. You don't have to extend Object in your class, that is automatically done for you. The object class contains a default constructor which is automatically called when you instantiate objects from classes that have no constructor.
In Java you cannot inherit from more than one class (there is no direct multiple inheritance allowed).

The super keyword

If you have a class B that inherits from Class A, you can call a constructor of class A in any constructor of class B using the keyword super supplying the arguments that match those of the constructor which you want to call. To do that the constructor call with super must be the first statement of the constructor. Cutting to the chase,

Expand|Select|Wrap|Line Numbers
  1.  
  2.  
  3. class Product {
  4.  String code;
  5.  double price;
  6.  public Product() {
  7.   code = "No code";
  8.         price = 0.0;
  9.  }
  10.  public Product(String code, double price) {
  11.   this.code = code;
  12.         this.price = price;
  13.  }
  14.  public void setCode(String code) {
  15.   this.code = code;
  16.  }
  17.  public String getCode() {
  18.   return code;
  19.  }
  20.  public void setPrice(double price) {
  21.   this.price = price;
  22.  }
  23.  public double getPrice() {
  24.   return price;
  25.  }
  26. }
  27. class DVD extends Product {
  28.  String title;
  29. }
  30.  
  31.  
I have put there two classes. Product is looking well dressed indeed while a lot can be said about the DVD class. If we write another class (you should do that at this moment) called, say TestProducts, then we can create Products in its main method using any of the two constructors provided.

Expand|Select|Wrap|Line Numbers
  1. Product useless = new Product();
  2. Product better = new Product("0001", 2.50); //well you can call your variables anything that is not a keyword
Now it is said in the code that DVD extends Product. Let's explore the meaning:

Expand|Select|Wrap|Line Numbers
  1.  DVD dvd = new DVD();
This is allowed by the compiler. However, there is a trick here. new DVD() calls a constructor in the DVD class that takes no arguments. There is unfortunately no such thing in the code. That's when the super keyword is used. Seeing no constructor in the DVD class, the compiler first inserts a default constructor for DVD (we did that in the last class) and then in that constructor, it inserts the line super();
This will call the default constructor in the direct super class of the class DVD which in this case is Product. So the code
Expand|Select|Wrap|Line Numbers
  1. DVD dvd = new DVD();
  2. System.out.println(dvd.getCode());
  3.  
prints “No code” to the screen because the default constructor of Product initializes code to the String "No code". That's the constructors trick in java.
When writing your classes make sure you keep these things in mind. Of course all your classes, except maybe the test class should have a constructor if you intend to use that class for creating objects.
Now watch this, change the DVD class to
Expand|Select|Wrap|Line Numbers
  1. class DVD extends Product {
  2.  String title;
  3.  public DVD (String title) {
  4.   this.title = title;
  5.  }
  6. }
When you compile you get, cannot find symbol : constructor DVD().
This illustrates two important points. We've already looked at the first point which is that if a class contains at least one constructor, then the default constructor is not automatically created by the compiler. That is why the compiler said there is no no-arg constructor for DVD.
The second point is that constructors are not inherited. The super class Product contains a default constructor but that is ignored by the compiler in the DVD class because there now is a constructor in DVD and so none has been added which calls the super class constructor. Now that is something every Java programmer must fully understand.

A more decent DVD class is
Expand|Select|Wrap|Line Numbers
  1. class DVD extends Product {
  2.  String title;
  3.  public DVD() {
  4.   super();
  5.   title = "No title";
  6.  }
  7.  public DVD (String code, double price, String title) {
  8.   super(code, price);
  9.   this.title = title;
  10.  }
  11.  public void setTitle(String title) {
  12.   this.title;
  13.  }
  14. }
Notice the proper use of super. In the no-arg constructor, the call to super initializes code and price as specified by the no-arg constructor of Product. After that the constructor initializes title which is unique to DVDs only. The second constructor performs a similar act but this time calling the two argument constructor in Product. This shows one big advantage of inheritance. Code reuse. Instead of re-declaring code and price in DVD, by saying DVD extends Product we have already said that we have these two attributes in DVD. Instead of initializing code and price in the DVD class, we simply use the super keyword to call the code that we have already written that does that. In this particular example, all the methods (the getters and setters) that are in Product are also available in the class DVD. We have inherited everything from Product into DVD and added our
own attribute, title. At this point you may want to appreciate the difference between the keywords ‘this’ and ‘super’. Recall that ‘this’ calls a constructor in the same class, and must be the first statement of the constructor while ‘super’ calls the immediate super class' constructor and must also be the first line of the constructor.

When to use inheritance

Now that you know how inheritance is done let's look at when it is done. Ah but first why use Inheritance? Inheritance promotes code reuse and polymorphism.
We've already seen the code reuse part where you don't keep defining the same things you've defined before when they all really have the same definition. We look at polymorphism shortly. So when you want to use it is basically when you have a general type and several more specific types of the general type. The relationship to use is the is-A relationship. A DVD is-A product so DVD extends Product. A secretary is-An employee so Secretary extends Employee. An employee is a person so Employee extends Person. I'm sure you can now come up with other less boring examples.

Access modifiers and inheritance

I have hinted at the possibility of subclasses not being able to access all the properties and methods of their super classes. This is done using access modifiers.

public - A variable (includes method) marked public is available in all classes both in the same package and in other packages as well. It is available to the public.
protected - variables marked protected can be accessed within the class to which they belong and in subclasses of that class. Remember that this the default access if none is specified.
private - private variables are visible only within the class to which they belong.

Expand|Select|Wrap|Line Numbers
  1.  
  2.  
  3. class Product {
  4.  private String code;
  5.  private double price;
  6.  public Product() {
  7.   code = "No code";
  8.         price = 0.0;
  9.  }
  10.  public Product(String code, double price) {
  11.   this.code = code;
  12.         this.price = price;
  13.  }
  14.  
  15. }
  16. class DVD extends Product {
  17.  String title;
  18.  public DVD() {
  19.   super();
  20.   title = "No title";
  21.  }
  22.  public DVD (String code, double price, String title) {
  23.   super(code, price);
  24.   this.title = title;
  25.  }
  26. }
  27.  
  28.  
  29.  
Then doing
Expand|Select|Wrap|Line Numbers
  1. DVD dvd = new DVD();
  2. System.out.println(dvd.code);
  3.  
will get the compiler complaining about trying to access the private member code of Product in DVD. If you're shy about someone and you happen to come across them in a corridor and don't know what to think about, you can debate (silently of course) with yourself whether the private variables are inherited and not accessible in subclasses or are simply not inherited at all.

Let's look back at encapsulation again at this moment and add a bit more stuff on it. I said Java code is encapsulated into classes and class attributes are encapsulated into methods and fields. In good programs, data should be available only where it is required. You should not move around and expose data unnecessarily in your programs. Some data should be hidden. If a person wants to know the price of a product, give them the price and not all the data about the product. Giving out all the product data exposes other information in the product class which we may want to keep private. If we have a biased
calculate price method in our product, we certainly wouldn't want everyone to see it. That's where we use the private keyword. If we define all attributes and methods in our class as private, then no other class can access those properties and methods from outside that class. Inheriting from our class is useless since we cannot access anything from the super class. Typically, you will see fields in a class defined as private and then have public methods for getting those fields or for setting their values. Like this
Expand|Select|Wrap|Line Numbers
  1. class Product {
  2.  private String code;
  3.  private double price;
  4.  public double getPrice() {
  5.   return price;
  6.  }
  7.  public String getCode() {
  8.   return code;
  9.  }
  10. }
  11. class DVD extends Product {
  12. }
This means all classes that extend Product cannot access code and prices attributes directly using the dot operator but can do so using the getters provided because these getters are public and so are accessible in the subclasses.
Expand|Select|Wrap|Line Numbers
  1. DVD d = new DVD();
  2. d.code;
  3.  
will give an error but
Expand|Select|Wrap|Line Numbers
  1. d.getCode(); 
is allowed because getCode() is public.
You will often see getter and setter methods for each private field defined in the class. This is very bad. It defeats the purpose of making the fields private in the first place. you should provide getters and setters only to fields which you want to be accessible in subclasses.

Expand|Select|Wrap|Line Numbers
  1.  
  2.  
  3. class Product {
  4.  private String code;
  5.  private double price;
  6.  private final double addedProfit = 500;
  7.  
  8.  public Product(String code, double price) {
  9.    this.code = code;
  10.          this.price = price;
  11.  }
  12.  public double getPrice() {
  13.   return price + addedProfit;
  14.  }
  15.  public String getCode() {
  16.   return code;
  17.  }
  18. }
  19. class DVD extends Product {
  20.  String title;
  21.  public DVD(String code, double price, String title) {
  22.   super(code, price);
  23.   title = title;
  24.  }
  25.  
  26. }
  27.  
In this Product class the added profit is kept secret even to extending classes. People can get the price but do not know that an added profit has been included in the price.
There is no getter for the addedProfit field. Now
Expand|Select|Wrap|Line Numbers
  1. DVD d = new DVD("001", 500, "Test");
  2. System.out.println(d.getPrice());
  3.  
prints 1000.0. the DVD class knows nothing about the added profit, but each time a DVD is created with price x, getPrice on it will always give x + 500. This demonstrates that private fields are actually inherited but not accessible in subclasses. The added profit is there in DVDs but the field addedProfit is not accessible in DVD classes. Isn't that neat? Practice these things and get them clearly, they are the small things that keep us coming back to the Java compiler again and again and again.

Let's take a break from inheritance and come back again to look at overriding

Aside: Arrays and Strings

When many items of data of the same type have to be stored, there is an array type in Java to store the data more efficiently than having many separate variables.
If you want to store ten scores for someone you could define ten int variables with:
Expand|Select|Wrap|Line Numbers
  1. int score1;
  2. int score2;
  3. int score3;
  4. //...e.t.c
  5.  
You would get fatigue if you use the same method for storing 500 scores. So the application shows itself in the design. Whenever you need to store many things in your
program you need a data type which is capable of storing many items in one variable, an array is one option. Now arrays can only store items of the same type. You cannot store an
int and a double in the same array. You can store objects of class type(or interface type) in an array. So you can have an array of products, an array of DVDs e.t.c. Another important property of arrays is that arrays have a fixed size. An array object can only store the same number of objects. To define an array of int of size 10 you do
Expand|Select|Wrap|Line Numbers
  1. int[] scores = new int[10];
  2.  
or
Expand|Select|Wrap|Line Numbers
  1. int scores[] = new int[10];
  2.  
I prefer the first method because then you can read it as int array, scores is an array of size 10.
You can declare an array without initializing it using int[] scores; you can then initialize it later on in the code when you know what size it should be. To add items to an initialized array, you need to know the position in the array where you want to place the item first. Then you simply assign as
Expand|Select|Wrap|Line Numbers
  1. scores[index] = value;
  2.  
where index is an integer and value is of the array's type. Array indexing starts at zero. Note that the array index can be byte, char, int but not long. The guys at Sun just decided that they won't allow long type as index and that's that. If you try to set a value at a position outside the size of the array, the code compiles but will give an Exception (we will look at exceptions in the next article) at run time. So array types are checked at compile time but the array indices are checked at run time. The following bits of code demonstrate these things:

Expand|Select|Wrap|Line Numbers
  1.  
  2.  
  3. double[] test; //declaration without initialization
  4. int i = 5;
  5. long l = 20l;
  6. test = new double[i]; //initializing the array same as test = new double[5];
  7.  
  8. //test = new double[l]; error. long cannot be used as array index 
  9.  
  10. test[0] = 3.5; // the first position in the array is now filled with the double 3.5
  11. test[5] = 4.0 //compiles but gives runtime exception
  12.  
  13.  
You should commit to your heart that if you define an array as
Expand|Select|Wrap|Line Numbers
  1. Type[] array = new Type[x];
  2.  
where Type is the type of the array (primitive or otherwise) and x is a valid index value, then you get an array with accessible positions array[0], array[1], array[2], ...,array[x-1]. These are x positions in the array(from 0 to x-1). accessing array[x] will give the runtime exception. Arrays can be of any dimension.
To declare an n dimension array just use n [] for the declaration. e.g. a 2 dim int array is defined as
Expand|Select|Wrap|Line Numbers
  1. int[][] twoDim = new int[5][8];
  2.  
Yes you are allowed to write
Expand|Select|Wrap|Line Numbers
  1. int twoDim[][] = new int[5][8];
  2.  
Notice that
Expand|Select|Wrap|Line Numbers
  1. int[][] twoDim = new int[5][];
  2.  
is allowed but
Expand|Select|Wrap|Line Numbers
  1. int[][] twoDim = new int[][5];
  2.  
is not allowed. You will enjoy researching the reason for this.

Let's bring those strings back into the picture again. You might already be thinking that a String is simply an array of characters, if you are then good for you. Some methods in the String class return arrays. For example to break up a string into its constituent characters, just call the toCharArray() method as follows
Expand|Select|Wrap|Line Numbers
  1. String abc = "abc";
  2. char[] chars = abc.toCharArray();
and chars is now a char array of size 3 with chars[0] == 'a', chars[1] == 'b' and chars[2] == 'c';

A more useful method is the split method in the String class which splits a string into substrings based on the arguments you supply to this method. The simplest example is to split a string using a character, most commonly the space character.
Expand|Select|Wrap|Line Numbers
  1. String s = "This is a stupid string";
  2. String[] stupid = s.split(" ");
Here s.split(" "); calls the split() method with the argument " " (space). This will break up the string s using space into an array of strings. So in this case the array stupid now contains the words that made up the sentence s. Do not fail to use this method when the need arises. It will simplify most of your work with strings.
There is a class in the java.util package called Arrays which has some useful methods to use with arrays. The key methods there are the sort, the binary search and the toString. You should write a program to test their use
and familiarize yourself with them.

Now let's get back to inheritance and pick it up on overriding.
Dfn :Overriding is when an instance (non-static) method in a class has the same signature (name argument types and order) as a method in that class's parent class and the same return type as well. As of 1.5, an overriding method can also return a subtype of the type returned by the overridden method. This is called a covariant return type.

Expand|Select|Wrap|Line Numbers
  1. class A  {
  2.    int a = 0;;
  3.    public int getA() {
  4.     return a;
  5.     }
  6.   }
  7.   class B extends A {
  8.    public int getA () {
  9.   return a + 2;
  10.    }
  11.   }
Expand|Select|Wrap|Line Numbers
  1. new A().getA(); 
returns 0
but
Expand|Select|Wrap|Line Numbers
  1. new B().getA(); 
returns 2;
Some points to note.
Overriding occurs when you have inheritance. Without inheritance there is no super class method to override. As of 1.5 this compiles
Expand|Select|Wrap|Line Numbers
  1. 1. class A  {
  2. 2.  int a;
  3. 3.  public Object getA() {
  4. 4.  return a;
  5. 5.     }
  6. 6.   }
  7. 7. class B extends A {
  8. 8. public Number getA () {
  9. 9.  return a + 2;
  10. 10.  }
  11. 11. }
Notice how a in class A is automatically boxed up to Integer which in turn is accepted as a correct return type. essentially line 4 is treated as
Expand|Select|Wrap|Line Numbers
  1. return new Integer(a);
Now the overriding method in class B returns an object of type Number which is a subclass of the class Object so the override is accepted by the compiler.
Feb 5 '07 #1
Share this Article
Share on Google+