Chapter 11. More Object-Oriented Programming

This chapter is the second part of our whirlwind tour (theoretical and practical) of OOP. We have already briefly discussed the concepts of encapsulation, inheritance, and polymorphism, but in this chapter, we will get to see them in action in some demo apps. While the working examples will show these concepts in their simplest forms, it will still be a significant stepping stone toward taking control of our layouts via our Java code.

In this chapter, we will explore the following topics:

  • Encapsulation in depth and how it helps us
  • Inheritance in depth and how to take full advantage of it
  • Polymorphism explained in greater detail
  • Static classes and how we have been using them already
  • Abstract classes and interfaces

First, we will handle encapsulation.

Remember that encapsulation thing?

So far, what we have really seen is what amounts to a kind of code-organizing convention where we write classes, full of variables and methods. We did discuss the wider goals of all this OOP stuff, but now we will take things further and begin to see how we actually manage to achieve encapsulation with OOP.

Tip

Definition of encapsulation

Keeping the internal workings of your code safe from interference from the programs that use it, allowing only the variables and methods you choose to be accessed. This means your code can always be updated, extended, or improved without affecting the programs that use it—provided the exposed parts are still made available in the same way. It also allows the code that uses your encapsulated code to be much simpler and easier to maintain because much of the complexity of the task is encapsulated in your code.

"But didn't you say we don't have to know what is going on inside?" So, you might question what we have seen so far like this: if we are constantly setting the instance variables like this: rambo.health = 100;, isn't it possible that eventually things could start to go wrong, perhaps like this:

rambo.soldierType = "fluffy bunny";

Encapsulation protects your class/objects of your class/code from being used in a way that it wasn't meant to be. By controlling the way that your code is used, it can only ever do what you want it to do and with value ranges that you can control.

It can't be forced into errors or crashes. Also, you are then free to make changes to the way your code works internally, without breaking the rest of your program or any programs that are using an older version of the code:

weightlifter.legstrength = 100;
weightlifter.armstrength = 1;
weightlifter.liftHeavyWeight();

// one typo and weightlifter rips own arms off

Encapsulation is not just vital for writing code that other people will use (like the Android API that we use); it is also essential when writing code we will reuse ourselves, as it will save us from our own mistakes. Furthermore, a team of programmers will use encapsulation extensively so that different members of the team can work on the same program without all members of the team knowing how the other team member's code works. We can encapsulate our classes for this same advantage, and here is how.

Controlling class use with access modifiers

The designer of the class controls what can be seen and manipulated by any program that uses their class. We can add an access modifier before the class keyword, like this:

public class Soldier{
   //Implementation goes here
}

Class access modifiers

There are two main access modifiers for classes in the context we have discussed so far. Let's briefly look at each in turn:

  • public: This is straightforward. A class declared as public can be seen by all other classes.
  • default: A class has default access when no access modifier is specified. This will make it public, but only to classes in the same package, and inaccessible to all others.

So, now we can make a start at this encapsulation thing. But even at a glance, the access modifiers described thus far are not very fine-grained. We seem to be limited to complete lockdown to anything outside the package or a complete free-for-all.

Actually, the benefits here are easily taken advantage of. The idea would be to design a package of classes that fulfills a set of tasks. Then, all the complex inner workings of the package – the stuff that shouldn't be messed with by anybody but our package – should be default access (only accessible to classes within the package). We can then make a careful choice of public classes available that can be used by others (or other distinct parts of our program).

Tip

For the size and complexity of the apps in this book, creating multiple packages is overkill. We will, of course, be using other people's packages and classes, so this stuff is worth knowing.

Class access in summary

A well-designed app will probably consist of one or more packages, each containing only default or default and public classes.

In addition to class-level privacy controls, Java gives us programmers very fine-grained controls, but to use these controls, we have to look into variables with a little more detail.

Controlling variable use with access modifiers

To build on the class visibility controls, we have variable access modifiers. Here is a variable with the private access modifier being declared:

private int myInt;

Also note that all our discussion of variable access modifiers applies to object variables, too. For example, here is an instance of our Soldier class being declared, created, and assigned. As you can see, the access specified in this case is public:

public Soldier mySoldier = new Soldier();

Before you apply a modifier to a variable, you must first consider class visibility. If class a is not visible to class b – say, because class a has default access and class b is in another package – then it doesn't make any difference what access modifiers you use on the variables in class a: class b can't see any of them.

So, it makes sense to show a class to another class when necessary, but only to expose the variables that are needed – not everything.

Here is an explanation of the different variable access modifiers.

Variable access modifiers

Variable access modifiers are more numerous and finely grained than the class access modifiers. The depth and complexity of access modification is not so much in the range of modifiers, but rather in the smart ways we can combine them to achieve the worthy goals of encapsulation. Here are the variable access modifiers:

  • public: You guessed it: any class or method from any package can see this variable. Use public only when you are sure that this is what you want.
  • protected: This is the next least restrictive after public. Protected variables can be seen by any class and any method as long as they are in the same package.
  • default: Default doesn't sound as restrictive as protected, but it is more so. A variable has default access when no access is specified. The fact that default is restrictive perhaps implies that we should be thinking on the side of hiding our variables more than we should be exposing them. At this point, we need to introduce a new concept. Do you remember that we briefly discussed inheritance and how we can quickly take on the attributes of a class and yet refine it by using the extends keyword? Just for the record, default access variables are not visible to subclasses; that is, when we extend a class like we did with Activity, we cannot see its default variables. We will look at inheritance in more detail later in this chapter.
  • private: Private variables can only be seen within the class they are declared. As with default access, they cannot be seen by subclasses (classes that inherit from the class in question).

Variable access summary

A well-designed app will probably consist of one or more packages, each containing only default or default and public classes. Within these classes, variables will have carefully chosen and varied access modifiers, chosen with a view to achieving our goal of encapsulation.

There's one more twist in all this access modification stuff that we must look at before we get practical with it.

Methods have access modifiers too

We already briefly mentioned in Chapter 9, Java Methods, that methods have access modifiers. This makes sense since methods are the things that our classes can do. We will want to control what users of our class can and can't do.

The general idea here is that some methods will do things internally only and are therefore not needed by users of the class. Some methods will be fundamental to how users of the class use your class.

Method access modifiers

The access modifiers for methods are the same as for the class variables. This makes things easy to remember but suggests, again, that successful encapsulation is a matter of design rather than of following any specific set of rules.

As an example, this method, provided it is in a public class, could be used by any other class:

public useMeEverybody(){
   //do something everyone needs to do here
}

On the other hand, this method could only be used internally by the class that created it:

private secretInternalTask(){
   /*
         do something that helps the class function internally
         Perhaps, if it is part of the same class,
         useMeEverybody could use this method...
         On behalf of the classes outside of this class.
         Neat!
   */
}

And the following method with no access specified has default visibility. It can be used only by other classes in the same package. If we extend the class, holding this default access method, the class will not have access to this method:

fairlySecretTask(){
   // allow just the classes in the package
   // Not for external use
}

As a last example before we move on, here is a protected method, only visible to the package, but usable by our classes that extend it – just like onCreate:

protected packageTask(){
   // Allow just the classes in the package
   // And you can use me if you extend me too
}

Let's have a quick recap on method encapsulation. Remember, you don't need to memorize everything.

Method access summary

Method access should be chosen to best enforce the principles we have already discussed. It should provide the users of your class with just the access they need and preferably nothing more. That way, we achieve our encapsulation goals, such as keeping the internal workings of our code safe from interference from the programs that use it, for all the reasons we have discussed.

Accessing private variables with getters and setters

Now we need to consider, if it is a best practice to hide our variables away as private, how would we allow access to them without spoiling our encapsulation. What if an object of the Hospital class wanted access to the health member variable from an object of the Soldier type so that it could increase it? The health variable should be private because we don't want just any piece of code changing it.

To be able to make as many member variables as possible private and yet still allow some kind of limited access to some of them, we use getters and setters. Getters and setters are just methods that get and set variable values.

This is not some special new Java thing that we have to learn. It is just a convention for using what we already know. Let's have a look at getters and setters by using our Soldier and Hospital class example.

In this example, each of our two classes are created in their own file but in the same package. First of all, here is our hypothetical Hospital class:

class Hospital{

   private void healSoldier(Soldier soldierToHeal){
         int health = soldierToHeal.getHealth();
         health = health + 10;
         soldierToHeal.setHealth(health);
   }

}

Our implementation of the Hospital class has just one method, healSoldier. It receives a reference to a Soldier object as a parameter. So, this method will work on whichever Soldier object is passed in: vassily, wellington, rambo, or whoever.

It also has a local health variable, which it uses to temporarily hold and increase the soldier's health. In the same line, it initializes the health variable to the Soldier object's current health. The Soldier object's health is private, so the public getter method, getHealth, is used instead.

Then, health is increased by 10 and the setHealth setter method loads up the new revived health value, back to the Soldier object.

The key here is that although a Hospital object can change a Soldier object's health, it only does so within the bounds of the getter and setter methods. The getter and setter methods can be written to control and check for potentially mistaken – even harmful – values.

Next, let's look at our hypothetical Soldier class with the simplest implementation possible of its getter and setter methods:

public class Soldier{
   
   private int health;

   public int getHealth(){
         return health;
   }

   public void setHealth(int newHealth){

         // Check for stupid values of newHealth
         health = newHealth;
   }
}

We have one instance variable called health, and it is private. Private means that it can only be changed by methods of the Soldier class. We then have a public getHealth method, which returns the value held in the private health int variable. As this method is public, any code with access to an object of the Soldier type can use it.

Next, the setHealth method is implemented. Again, it is public, but this time it takes an int as a parameter and assigns whatever is passed in to the private health variable. In a more life-like example, we would write some more code here to make sure that the value passed in is within the bounds we expect.

Now, we declare, create, and assign to make an object of each of our two new classes and see how our getters and setters work:

Soldier mySoldier = new Soldier();
// mySoldier.health = 100;//Doesn't work, private

// we can use the public setter setHealth() instead
mySoldier.setHealth(100); //That's better

Hospital militaryHospital = new Hospital();

// Oh no mySoldier has been wounded
mySoldier.setHealth(10);

/*        
   Take him to the hospital.
   But my health variable is private
   And Hospital won't be able to access it
   I'm doomed - tell Laura I love her

   No wait- what about my public getters and setters?
   We can use the public getters and setters 
   from another class
*/

militaryHospital.healSoldier(mySoldier);

// mySoldiers private variable health has been increased 
// by 10. I'm feeling much better thanks!

We can see that we can call our public setHealth and getHealth methods directly on our object of the Soldier type. Not only that, we can call the healSoldier method of the Hospital object, passing in a reference to the Soldier object, which too can use the public getters and setters to manipulate the private health variable.

We can see that the private health variable is simply accessible, yet totally within the control of the designer of the Soldier class.

If you want to play around with this example, there is the code for a working app in the code bundle in the Chapter 11 folder called GettersAndSetters. I have added a few lines of code to print to the console. We deliberately covered this the way we did to keep the key parts of the code as clear as possible.

Note

Getters and setters are sometimes referred to by their more correct names, Accessors and Mutators. We will stick to getters and setters. I just thought you might like to know the jargon.

Yet again, our example and the explanation are probably raising more questions. That's good.

By using encapsulation features (such as access control), it is like signing a really important deal about how to use and access a class, its methods, and variables. The contract is not just an agreement about now, but an implied guarantee for the future. We will see that, as we go ahead through this chapter, there are more ways that we refine and strengthen this contract.

Tip

Use encapsulation where it is needed or, of course, if you are being paid to use it by an employer. Often, encapsulation is overkill on small learning projects, such as some of the examples in this book – except, of course, when the topic you are learning is encapsulation itself.

We are learning this Java OOP stuff under the assumption that you will one day want to write much more complex apps, whether on Android or some other platform which uses OOP. In addition, we will be using classes from the Android API that use it extensively, and this will help us understand what is happening then as well. Typically, throughout this book, we will use encapsulation when implementing full projects and often overlook it when showing small code samples to demonstrate a single idea or topic.

Setting up our objects with constructors

With all these private variables and their getters and setters, does it mean that we need a getter and a setter for every private variable? What about a class with lots of variables that need initializing at the start. Think about the following:

mySoldier.name
mysoldier.type
mySoldier.weapon
mySoldier.regiment
...

Some of these variables might need getters and setters, but what if we just want to set things up when the object is first created to make the object function correctly?

Surely, we don't need two methods (a getter and a setter) for each?

Fortunately, this is unnecessary. For solving this potential problem, there is a special method called a constructor. We briefly mentioned the existence of a constructor when we discussed instantiating an object from a class in Chapter 10, Object-Oriented programming. Let's look at constructors once more.

Here, we create an object of the Soldier type and assign it to an object called mySoldier:

Soldier mySoldier = new Soldier();

Nothing new here, but look at the last part of that line of code:

...Soldier();

This looks suspiciously like a method.

All along, we have been calling a special method called a constructor that has been created, behind the scenes, automatically, by the compiler.

However – and this is getting to the point now – as with a method, we can override it, which means we can do useful things to set up our new object before it is used. The following code shows how we could do this:

public Soldier(){
   // Someone is creating a new Soldier object
   
   health = 200;
   // more setup here
}

The constructor has a lot of syntactical similarities to a method. It can, however, only be called with the use of the new keyword, and it is created for us automatically by the compiler – unless we create our own, as in the previous code.

Constructors have the following features:

  • They have no return type
  • They have the exact same name as the class
  • They can have parameters
  • They can be overloaded

One more piece of Java syntax that is useful to introduce at this point is the Java this keyword.

The this keyword is used when we want to be explicit about exactly which variables we are referring to. Look at this example constructor, again for a hypothetical variation of the Soldier class:

public class Soldier{

   String name;
   String type;
   int health;

   // This is the constructor
   // It is called when a new instance is created
   public Soldier(String name, String type, int health){

          // Someone is creating a new Soldier object
          
          this.name = name;
          this.type = type;
          this.health = health;

          // more setup here
   }
}

This time, the constructor has a parameter for each of the variables we want to initialize. By using the this keyword, it is clear when we mean the member variable or the parameter.

There are more twists and turns to be learned about variables and this, and they make much more sense when applied to a practical project. In the next app, we will explore all we have learned so far in this chapter and some more new ideas too.

First, let's look at a bit more OOP.

Static methods

We know quite a lot about classes already. For example, we know how to turn them into objects and use their methods and variables. But something isn't quite right. Since the very start of this book, we have been using two classes more than any other. We have repeatedly used Log and Toast to output to the logcat or the users screen, but have not instantiated them once! How can this be? We never did this:

Log myLog  = new Log();
Toast myToast = new Toast();

We just went ahead and used the classes directly, like this:

Log.i("info","our message here");
Toast.makeText(this, "our message",      
Toast.LENGTH_SHORT).show();

The static methods of classes can be used without first instantiating an object of the class. We can think of this as a static method belonging to the class and all other methods belonging to an object/instance of a class.

And, as you have probably realized by now, Log and Toast both contain static methods. To be clear: Log and Toast contain static methods; they themselves are still classes.

Classes can have both static and regular methods as well, but the regular methods would need to be used in the regular way, via an instance/object of the class.

Let's take another look at Log.i in action:

Log.i("info","our message here");

Here, i is the method being statically accessed, and the method takes two parameters, both of type String.

Next, we can see the makeText static method of the Toast class in use:

Toast.makeText(this, "our message", 
      Toast.LENGTH_SHORT).show();

The makeText method of the Toast class takes three arguments.

First is this, which takes some explaining. We saw when talking about constructors that to explicitly refer to the member variables of the current instance of an object, we can use this.health, this.regiment, and so on.

When we use this as we do in the previous line of code, we are referring to the instance of the class itself. Not the Toast class, but the this in the previous line of code is a reference to the class the method is being used from. In our case, we have used it from MainActivity.

Many things in Android require a reference to an instance of Activity to do its job. We will – fairly regularly throughout this book – pass in this (a reference to the Activity) in order to help a class/object from the Android API do its work. We will also write classes that need this as an argument in one or more of its methods. So, we will see how to handle this when it is passed in as well.

The second argument is, of course, a String.

The third argument is accessing a final variable, LENGTH_SHORT, again via the class name, not an instance of the class. This is achieved if we declare a variable like we do in the following line of code:

public static final int LENGTH_SHORT = 1;

If the variable was declared in a class called MyClass, we could access the variable like this: MyClass.LENGTH_SHORT, and use it like any other variable, but the final keyword makes sure that the value of the variable can never be changed. This type of variable is called a constant.

The static keyword also has another consequence for a variable, especially when it is not a constant (can be changed), and we will see this in action in our next app.

Now, if you look carefully at the very end of the line of code that shows a Toast message to the user, you will see something else new, .show().

This is called chaining, and all we are doing is calling a second method of the Toast class but using just one line of code. It is the show method that actually triggers the message.

We will see some more chaining as we proceed through this book, like in Chapter 14, Android Dialog , when we make pop-up dialogs.

Tip

If you want to read about the Toast class and some of its other methods in detail, you can do so here: http://developer.android.com/reference/android/widget/Toast.html.

Static methods are often provided in classes that have uses that are so generic that it doesn't make sense to have to create an object of the class. Another really useful class with static methods is Math. This class is actually a part of the Java API, not the Android API.

Tip

Want to write a calculator app? It's easier than you think with the static methods of the Math class. You can look at them here: http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html.

If you try this out, you will need to import the Math class in the same way that we imported all the other classes we have used.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.153.224