Casting and Converting Objects and Primitive Types
One thing you discover quickly about Java is how finicky it is about the information it will handle. Like Morris, the perpetually hard-to-please cat in the 9Lives™ Cat Food commercials, Java expects things to be a certain way and won't put up with alternatives.
When you are sending arguments to methods or using variables in expressions, you must use variables of the right data types. If a method requires an int, the Java compiler responds with an error if you try to send a float value to the method. Likewise, if you're setting up one variable with the value of another, they must be of the same type.
There is one area where Java's compiler is decidedly unMorris-like: Strings. String handling in println() methods, assignment statements, and method arguments is simplified by the concatenation operator (+). If any variable in a group of concatenated variables is a string, Java treats the whole thing as a String. This makes the following possible:
float gpa = 2.25F; System.out.println("Honest, dad, my GPA is a " + (gpa+1.5));
Sometimes you'll have a value in your Java program that isn't the right type for what you need. It might be the wrong class or the wrong data type, such as a float when you need an int.
You use casting to convert a value from one type to another.
Casting is the process of producing a new value that has a different type than its source. The word's meaning is similar to that regarding acting, where a character on a TV show can be recast with another actor after a salary dispute or an unfortunate public lewdness arrest.
Although the concept of casting is reasonably simple, the usage is complicated by the fact that Java has both primitive types (such as int, float, and boolean) and object types (String, Point, ZipFile, and the like). There are three forms of casts and conversions to talk about in this section:
Casting between primitive types, such as int to float, or float to double
Casting from an instance of a class to an instance of another class
Casting primitive types to objects and then extracting primitive values from those objects
When discussing casting, it can be easier to think in terms of sources and destinations. The source is the variable being cast into another type. The destination is the result.
Casting Primitive Types
Casting between primitive types enables you to convert the value of one type to another primitive type. It most commonly occurs with the numeric types, and there's one primitive type that can never be used in a cast. Boolean values must be either true or false and cannot be used in a casting operation.
In many casts between primitive types, the destination can hold larger values than the source, so the value is converted easily. An example would be casting a byte into an int. Because a byte holds values from 128 to 127 and an int holds from -2.1 billion to 2.1 billion, there's more than enough room to cast a byte into an int.
You can often automatically use a byte or a char as an int; you can use an int as a long, an int as a float, or anything as a double. In most cases, because the larger type provides more precision than the smaller, no loss of information occurs as a result. The exception is casting integers to floating-point values; casting an int or a long to a float, or a long to a double, can cause some loss of precision.
A character can be used as an int because each character has a corresponding numeric code that represents its position in the character set. If the variable i has the value 65, the cast (char)i produces the character value 'A'. The numeric code associated with a capital A is 65, according to the ASCII character set, and Java adopted this as part of its character support.
You must use an explicit cast to convert a value in a large type to a smaller type, or else converting that value might result in a loss of precision. Explicit casts take the following form:
In the preceding example, typename is the name of the data type you're converting to, such as short, int, or float. value is an expression that results in the value of the source type. For example, the value of x is divided by the value of y, and the result is cast into an int in the following expression:
(int)(x / y);
Note that because the precedence of casting is higher than that of arithmetic, you have to use parentheses here; otherwise, the value of x would be cast into an int first and then divided by y, which could easily produce a different result.
Instances of classes also can be cast into instances of other classes, with one restriction: The source and destination classes must be related by inheritance; one class must be a subclass of the other.
Analogous to converting a primitive value to a larger type, some objects might not need to be cast explicitly. In particular, because a subclass contains all the same information as its superclass, you can use an instance of a subclass anywhere a superclass is expected.
For example, consider a method that takes two arguments, one of type Object and another of type Window. You can pass an instance of any class for the Object argument because all Java classes are subclasses of Object. For the Window argument, you can pass in its subclasses, such as Dialog, FileDialog, and Frame.
This is true anywhere in a program, not just inside method calls. If you had a variable defined as class Window, you could assign objects of that class or any of its subclasses to that variable without casting.
This is true in the reverse, and you can use a superclass when a subclass is expected. There is a catch, however: Because subclasses contain more behavior than their superclasses, there's a loss in precision involved. Those superclass objects might not have all the behavior needed to act in place of a subclass object. For example, if you have an operation that calls methods in objects of the class Integer, using an object of class Number won't include many methods specified in Integer. Errors occur if you try to call methods that the destination object doesn't have.
To use superclass objects where subclass objects are expected, you must cast them explicitly. You won't lose any information in the cast, but you gain all the methods and variables that the subclass defines. To cast an object to another class, you use the same operation as for primitive types:
In this case, classname is the name of the destination class, and object is a reference to the source object. Note that casting creates a reference to the old object of the type classname; the old object continues to exist as it did before.
The following example casts an instance of the class VicePresident to an instance of the class Employee; VicePresident is a subclass of Employee with more information, which here defines that the VicePresident has executive washroom privileges:
Employee emp = new Employee(); VicePresident veep = new VicePresident(); emp = veep; // no cast needed for upward use veep = (VicePresident)emp; // must cast explicitly
Casting one object is necessary whenever you use Java2D graphics operations. You must cast a Graphics object to a Graphics2D object before you can draw onscreen. The following example uses a Graphics object called screen to create a new Graphics2D object called screen2D:
Graphics2D screen2D = (Graphics2D)screen;
Graphics2D is a subclass of Graphics, and both are in the java.awt package. You explore the subject fully during Day 13, "Color, Fonts, and Graphics."
In addition to casting objects to classes, you also can cast objects to interfaces, but only if an object's class or one of its superclasses actually implements the interface. Casting an object to an interface means that you can call one of that interface's methods even if that object's class does not actually implement that interface.
Converting Primitive Types to Objects and Vice Versa
One thing you can't do under any circumstance is cast from an object to a primitive data type, or vice versa.
Primitive types and objects are very different things in Java, and you can't automatically cast between the two or use them interchangeably.
As an alternative, the java.lang package includes classes that correspond to each primitive data type: Float, Boolean, Byte, and so on. Most of these classes have the same names as the data types, except that the class names begin with a capital letter (Short instead of short, Double instead of double, and the like). Also, two classes have names that differ from the corresponding data type: Character is used for char variables and Integer for int variables.
Java treats the data types and their class versions very differently, and a program won't compile successfully if you use one when the other is expected.
Using the classes that correspond to each primitive type, you can create an object that holds the same value. The following statement creates an instance of the Integer class with the integer value 7801:
Integer dataCount = new Integer(7801);
After you have an object created in this manner, you can use it as you would any object (although you cannot change its value). When you want to use that value again as a primitive value, there are methods for that, as well. For example, if you wanted to get an int value from a dataCount object, the following statement would be apt:
int newCount = dataCount.intValue(); // returns 7801
A common translation you need in programs is converting a String to a numeric type, such as an integer. When you need an int as the result, this can be done by using the parseInt() class method of the Integer class. The String to convert is the only argument sent to the method, as in the following example:
String pennsylvania = "65000"; int penn = Integer.parseInt(pennsylvania);
The following classes can be used to work with objects instead of primitive data types: Boolean, Byte, Character, Double, Float, Integer, Long, Short, and Void.
The Void class represents nothing in Java, so there's no reason it would be used when translating between primitive values and objects. It's a placeholder for the void keyword, which is used in method definitions to indicate that the method does not return a value.