Java 7 for Absolute Beginners

(nextflipdebug5) #1

CHAPTER 4 ■ OPERATORS


In the second example, casting an int to a short, we must have a cast operator, to tell the compiler
that we really mean to do that. That's necessary because data loss can occur when casting from a type
with more bits (32 in this case) to a type with fewer bits (16 in this case). Suppose i equals 65537. s would
then equal 1. That's because the maximum value of a short is 65536. The JVM divides the original value
by the maximum value of the variable you're casting into and returns the remainder. As you can
imagine, that can produce havoc and be hard to track down. As a rule, don't use narrowing casts (which
place the value of a data type with more available bits into a data type with fewer available bits).
Widening casts (which place the value of a data type with fewer available bits into a data type with more
available bits) aren't a problem (though don't do it unless you have a reason for it), but narrowing casts
are dangerous.
The third example shows another hazard involved in casting: loss of precision. If you cast a float or a
double to an integral type (any of the numeric types with no floating point component), the JVM
truncates (that is, removes) the mantissa. So 12.34 becomes 12. Even if it was originally 12.999, it would
get truncated to 12. In other words, it doesn't round; it removes. Similarly, casting from a double to a
float can lose precision, because a double has more bits than a float and can therefore have greater
precision. Again, narrowing casts are risky. Hard-to-find errors can arise from narrowing casts. Don't do
it unless you must, and it's a good idea to add some defensive code (which we get to shortly).

■ Caution Widening casts are fine; however, narrowing casts are dangerous. Take steps to ensure that your
narrowing casts can't receive values that cause trouble.

The fourth example isn't a cast, but it shows how to get from a primitive (a char in this case) to a
String. The same pattern applies for all the primitives: First get a wrapper for the primitive and then use
the wrapper's toString method.
I mentioned defensive coding for narrowing casts. Defensive coding is a good idea any time you
can't be sure the provided value won't cause a problem. In fact, in some kinds of applications
(distributed applications are a prime example), it's standard practice to validate (that is, ensure workable
values for) all the arguments to a method. Listing 4-6 is an example of defensive coding for a narrowing
cast from an int to a byte:

Listing 4-6. Defensive coding for a narrowing cast

private static byte intToByte(int i) {
if (i > Byte.MAX_VALUE || i < Byte.MIN_VALUE) {
throw new IllegalArgumentException("integer argument " +
"is too large or too small to cast to a byte");
}
return (byte) i;
}

Then the code that calls intToByte can decide what to do about the problem. Moving the validation
to its own method can be a good idea, both to encapsulate each bit of validation and to permit multiple
methods to make use of the same bit of validation. Many systems have validation classes (and
sometimes packages) that offer a number of such methods, so that the methods in other classes can
make use of consistent validation. You get used to that kind of design as you learn to use object-oriented
languages, including Java.
Free download pdf