06 – Java Beans
So far in the nule.org Java tutorial, we actually learned much about what makes the language tick. The concepts discussed so far are simple, but what makes programming hard isn’t the mechanics of programming – anybody can learn syntax. Creating a programming requires you to break concepts down in your head so that you can manage a self contained “chunk” of the program completely in your gray-matter at one time. That means you need a technique to break complex problems into sets of simple problems that can be solved in such a manner that you can later reassemble the pieces and get your working program. More importantly (at least to your boss,) your pieces can be understood by you later, and hopefully by others as well! This will make your code easily enhanceable, and supportable.
By far the most important basic unit of code in Java is a class. We’ve only talked about normal public classes (there are others, and they are important, but would distract from the basics we’re talking about here), and we’ve talked some about the roles that classes can play. Over the course of the tutorial, a number of chapters will be dedicated to creating classes specific to these roles, but the first role we’ll talk about here is the role of a storage object, or a Java Bean.
This is possibly the simplest role a Java class can play at any level of development. Java Beans or just beans, as we’ll call them (not to be confused with Enterprise Java Beans, or EJBs! We’ll talk about those beasts later, but the concepts you learn here play an important role later.) use a common set of conventions, and are best kept as simple and free of other logic as possible. We’re not talking about databases or files or anything like that yet, we’re talking about classes that hold data only while the program executes for the purposes of the program. In the previous chapter, we created a simple class that abstracted the idea of a car, and it really had two properties that it stored that related to the concept of a car. These were it’s speed and it’s color.
Obviously that’s a dramatically simple representation of a car, and though good for illustrative purposes. Most of the beans that you will create, even in relatively simple applications, are more complex than the one we created. The concept however remains the same. The vast majority of class variables, which will remain private, and methods are purely dedicated to getting and/or setting properties of the bean. Let’s look at an even simpler version of our Car bean to recall what makes up a single property:
public class Car {
public Car() {
// Notice that the constructor is empty this time.
}
private String color;
public String getColor() {
return color;
}
public void setColor(String value) {
color = value;
}
}
Here, our property can just be called “color”, and we would describe this property as essentially readable and writable, as it has both “getter” and “setter” methods. Since this value has such open access, why not just make the instance variable public, you may be thinking. Before I mentioned that it is considered best practice not to do so and mentioned that letting external classes alter your variables. This is still the best reason, and on advanced topics we may mention things like threads concurrently accessing your class (which may require synchronization of variables), but perhaps a more mundane reason would be that you want, or need to control what your variable contain to ensure that your class behaves as expected.
In our simple class above, if we were to use it as written it would be possible to call the getColor() method of an instance of our Car class before we have initialized the variable of color. What would happen then actually depends upon the version of Java we use, but at best we would get a null value (basically meaning not initialized, null is a Java keyword that you’ll get plenty of use out of) and at worst, in older versions of Java our application could crash (actually with an error that means “variable not initialized”)! Because we may be part of a large team or even be trying to reuse our code years after writing it, this kind of unexpected result should be avoided where you can when you write a bean.
Using the bean properties above and some of our flow-control logic that we’re about to learn, we actually have a lot of say in how the data we contain is used. In our example, if we were modeling a real car “null” for a color wouldn’t make much sense. While you want to avoid performing too much logic in a bean, here’s an example where it’s OK to do a little thinking. Let’s modify our code a bit.
public class Car {
public Car() {
// Notice that the constructor is empty this time.
}
private String color = "primer";
public String getColor() {
return color;
}
public void setColor(String value) {
if (value == null) {
color = "Not a valid color, so we "+
"painted it mauve.";
} else {
color = value;
}
}
}
This looks a little more like it. Now, no matter at what point we call the class’s getColor() method it can never be null. It pays to be cautious with the values we get from a bean (and indeed, it would be tedious to perform this kind of checking on every setter method we create) but depending upon our application, it may be essential that we do check the value of any data passed to our bean. Say we’re enhancing our Car class. If you were coding a property to store the fuel level in the car you’d never want to allow a negative number. Nor would you want to allow a fuel value greater than the capacity of the car. Worse yet, you could get errors if you allowed someone to enter values that were later used in math equations, for example if that values was used to divide by zero.
Another place it is OK, in my opinion, to use some logic in a bean is where you can create a property (usually only readable) that aggregates other properties. For example if your Car class had a fuel level property (probably a float or double value), and perhaps four more to indicate the pressure in each of the tires. Clearly the car couldn’t move (well, not very well) if any of those values were zero. You may have a property as follows:
...
public boolean isCarWorking() {
// Assume that the following variables are defined
// fuelLevel, tireFrontDriverPressure,
// tireFrontPassengerPressure,
// tireRearDriverPressure,
// tireRearPassengerPressure
if (fuelLevel > 0.0 &&
tireFrontDriverPressure > 0.0 &&
tireFrontPassengerPressure > 0.0 &&
tireRearDriverPressure > 0.0 &&
tireRearPassengerPressure > 0.0)
return true;
} else {
return false;
}
...
It’s really not fair at this point to provide an example like this that uses concepts we haven’t discussed, like “if” statements and the “&&” function, which essentially makes sure all of the conditions we expressed are true. But hopefully you get a point. Here we have created a “getter” for a value called carWorking, that returns true if the car can run and false if it can’t. Obviously way too simple again, much more can go wrong with a car than having flat tires or being out of fuel, but in our example a potentially helpful aggregate function. This may also illustrate why in some cases it may be important to control what the values may be set to.
So in summary it’s a great idea to create simple Java classes, beans if you will, to abstract out concepts from your data. Logic within these beans is OK if used sparingly. But how do we do things with the beans if we don’t want to use logic in them? We’re going to find out very shortly. Unfortunately, first we need a diversion into the system of organization that Java uses to let us arrange our classes (and it’s classes) logically.