Chapter 4. Properties

Properties are named attributes or characteristics. They define the behavior and state of an object. For instance, the current temperature value of the Temperature object is a property, as are the high and low temperature thresholds of the GenericTemperatureThresholdAdapter from the previous chapter. Properties are referenced by their name and can have any type, including primitives such as int, and class and interface types such as java.awt.Color. The name of the low threshold property is LowThreshold, and its type is double. Properties are usually part of the persistent state of an object. This will be dealt with later in Chapter 5.

Properties are exposed to visual programming tools and scripting environments, as well as to traditional Java programming. They are manipulated in a visual programming tool through some kind of property-editing interface. In a scripting environment, properties are exposed through a field-style syntax such as Object.Property = value, and value = Object.Property. These are syntactical conveniences provided by the scripting environment. As a Beans developer, you’ll expose your properties as described in the rest of this chapter.

Accessing Properties

An object’s properties are accessed by calling methods that are defined for setting and getting the property value. Any property that can be read will have an associated method for getting its value. Likewise, any property that can be written will have an associated method for setting its value. We’ll see shortly that objects don’t always provide both access mechanisms for every property. Properties can be read/write, read-only, or write-only.

The methods used for getting and setting property values should conform to the standard design pattern for properties. These methods are allowed (but not required) to throw checked exceptions. The method signatures are as follows:

public void set<PropertyName>(<PropertyType> value);
public <PropertyType> get<PropertyName>();

The existence of a matching pair of methods that conform to this pattern represents a read/write property with the name <PropertyName> of the type <PropertyType>. If only the get() method exists, the property is considered to be read-only; if only the set() method exists, the property is considered to be write-only.

If the <PropertyType> is boolean, the get() method can be replaced or augmented with a method that uses the following signature:

public boolean is<PropertyName>();

Let’s look back at the Temperature class. The current temperature is stored within an object of type Temperature, but up until now we have not provided a way to access that value. So now we can add a read-only property called CurrentTemperature. The code for the Temperature class will now include the following:

package BeansBook.Simulator;

import java.util.Vector;

public class Temperature
{
   // the current temperature in Celsius
   protected double currentTemp = 22.2;

   [The rest of the existing code goes here]

   // the get method for property CurrentTemperature
   public double getCurrentTemperature()
   {
      return currentTemp;
   }

}

It is important to recognize that properties are not defined by data members of the object’s class. One reason for this is that it would break encapsulation. More importantly, properties can be computed when they are needed without having to be stored explicitly. In an earlier example we described a Thermometer class that tracked temperatures from two locations. We could define a property called NumberOfLocations. This is a read-only property that describes how many locations are being tracked by the thermometer. In this case we don’t need to explicitly store a data member for this property, we just know that the value is always 2. We could add a property get method to access this value, as follows:

public int getNumberOfLocations()
{
   return 2:
}

Although our thermometer isn’t a visual component yet, imagine that at some point it will be capable of displaying the temperature from one of its source thermometers. Let’s design the Thermometer so that it is capable of displaying temperatures in either Celsius or Fahrenheit. We call this property DisplayingCelsius, and we expose it using a boolean data type. Remember that this doesn’t refer to the way that the property is stored internally, only the way that the property is exposed externally. If Celsius is not used, then Fahrenheit is being used, and vice versa. We’ll put off implementing the property until later. So the code for the Thermometer class now looks like this:

package BeansBook.Simulator;

public class Thermometer implements TempChangeListener
{
   // a reference to the temperature object that we are monitoring
   protected Temperature theTemperature;

   Thermometer(Temperature temperature)
   {
      theTemperature = temperature;

      // register for temperature change events
      theTemperature.addTempChangeListener(this);
   }

   // handle the temperature change events
   public void tempChanged(TempChangedEvent evt)
   {
      // do something with the temperature that we can retrieve
      // by calling evt.getTemperature()
   }

// the get method for the DisplayingCelsius property
public boolean isDisplayingCelsius()
{
   ...
}

// an alternate get method for the DisplayingCelsius property
public boolean getDisplayingCelsius()
{
   return isDisplayingCelsius();
}

// the set method for the DisplayingCelsius property
public void setDisplayingCelsius(boolean value)
{
   ...
}
}

In this example I’ve provided both forms of the get method for the boolean property DisplayingCelsius. You’ll notice that the only method that has an implementation so far is getDisplayingCelsius(), which does nothing but call the isDisplayingCelsius() method. We’ll fill in the code later.

Tip

Whenever two methods are provided that perform the same function, it is a good idea to implement one in terms of the other. There is no requirement to do this, but it is a good programming practice. Following this guideline will eliminate the need to repeat code, and to modify multiple areas of code when the implementation changes.

Normally when a property value is changed, the object will react in some way. Later, when our Thermometer object is capable of displaying a temperature value, we will have to implement code that reacts to a change to the DisplayingCelsius property by redisplaying the temperature according to the temperature units being used (Celsius or Fahrenheit).

Get Developing Java Beans now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.