Title: Casting
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Casting

Primitive "Cast" Considerations considerations

In this section, we will consider what happens when we mix different data types. Consider the following piece of code:


  double dValue =1.0;
  float fValue = 1.0;
  long lValue = 1
  int iValue = 1;
  short sValue = 1;
  byte bValue = 1;

  // The following are allowed by the compiler
  dValue = fValue;
  dValue = bValue;
  lValue = iValue;
  sValue = bValue;
  // etc.


All of the above operations will work because we are storing a smaller variable into a larger variable. By the way, there are more workable combinations that the ones show above. You can order the above data types by the size of what it can store as follows:

double > float > long > int > short > byte

There is never a problem storing a variable with a different variable type as long as the storing variable type is to the left of the fetched variable in the above diagram.

On the other hand when the storing variable is "smaller" than the fetching variable, then the compiler will give you an error unless you use a cast operation to tell the compiler that you really want to do the next operation and you are aware that you can lose data. For example:
sValue = (short) lValue;

Where Casting is Needed


  double dValue =1.0;
  float fValue = 1.0;
  long lValue = 1
  int iValue = 1;
  short sValue = 1;
  byte bValue = 1;

  // The following are allowed by the compiler without any
  // complaints.  However, if you remove the (xxxx) cast
  // designation, the compiler will give you an error.
  // In effect the compiler knows that it is possible to
  // lose data which such operations, and it wants you the programmer
  // to verify that YES, you REALLY want to do this

  fValue = (float)dValue;
  bValue = (byte)dValue;
  iValue = (int)lValue;
  bValue = (byte)sValue;
  // etc.


More Casting Rules:

A few more rules:

The char datatype is kind of special in that it really isn't a number(it actually represents an alphabetic character), but it can be used like a number. Consequently casting is always needed between char and anything else:


  char cValue = 'a';
  short sValue = 48;

  // The following are allowed.  If your remove the
  // cast operation, the compiler will give an error.

  cValue = (char) sValue;
  sValue = (short) cValue;


See what happens if you print out cValue and sValue

Mixed Arithmetic

If you do any operation that combines 2 primitive datatypes the result is promoted to the larger datatype. For example consider the following:


  long lValue = 1;
  double dValue =1.0;
  int iValue = 1;

  dValue = dValue * lValue * iValue; // OK as is

  // The right hand side evaluates to a double and a cast
  // operation is needed to store it back to something less than a double.
  lValue = (long) (dValue * lValue * iValue);


Small Type Arithmetic

One detail is that the result of arithmetic is never smaller than an int type. This is true even if all of the operands in the arithmetic are smaller than an int. Consequently if the stored variable is smaller than an int, you will always need a cast. For example:


  short sValue = 1;
  byte  bValue = 1;

  // cast operations are needed here

  sValue = (short)(sValue + bValue);
  bValue  = (byte)(bValue * bValue);


booleans:

Note that booleans are never involved with arithmetic operations. The boolean type can only be used in a logical operation --- more on that later.

If you have these rules down then see if the following code makes sense.
Casting.java

casts with Literals:

The last Primitive Casting operations to be considered are between literals. Consider the following examples:


// the following are OK
double dValue=123;
double dValue = 123.2;

// The following will generate an errors
int  iValue = 123.2;   // This is to be expected
float fValue = 123.2;  // This makes no sense to me

// The following however, is OK
int   iValue = (int)123.2;
float fValue = 123.2F;
float fValue2= (float)123.2;


One last word about Primitive Casting

When you need to cast, the compiler is telling you that you might lose data. The compiler wants the programmer to say --- Yes its OK, I know what I am doing. Check out what happens in the following example that compiles:


class Casting{
  public static void main (String[] args){
 	long lValue = 130;
	byte b = (byte)lValue;
	System.out.println("The byte is " + b);
  }
}


Note that for other languages which aren't as tightly typed, you can lose data when you mix data types and the language silently does it. This leads to subtle errors. Java is attempting to keep you out of trouble by providing tighter control during the compilation stage. For example, in the C language automatic conversion happens when you execute code that looks something like:
char c;
long d = 300;
c = d;

Object Casting

Consider the example we covered earlier:
Shape-Rectangle-Square-Circle

In this example, we have the following relationships:


               Object
	          |
	          |
		  v
	       Shape
	       |    |
	       |    |
	       v    v
       Rectangle   Circle
          |
          |
	  v
       Square



General Rules for Object Casting

If you need a specific type of class. For example, you are calling some procedure and it is looking for some type of class like Object or Shape. In this case, its always OK to pass it a type of class that is subclassed (or derived )from the needed class. So if your need an Object or a Shape class, its OK to pass in a Circle, Rectangle or Square. No casting is needed. This makes sense, because you are providing more than was required and this is OK.

There are times when you have a collection of class instances that are declared to be of type Object, but actually originated from some subclassed (derived) class. In this case it is possible to cast the Object instance into something more derived such as a Square Object instance. If the Object originated from some other class, then an error will be found at Run Time.

The following concepts will become more meaningful when we discuss some of the collection classes such as the Vector class.


import exercise.*;
class Test
{
  public static void main (String[] args)
  {
    Square sq1 = new Square();
    Rectangle r1;
    Shape sh1;
    Object ob1;

    // The following will work with no cast
    r1 = sq1;
    sh1 = r1;
    sh1 = sq1;
    ob1 = r1;
    ob1 = sq1;

    System.out.println("Finished Class operations that work without a cast");

    // The following will work with a cast
    Object ob2 = new Square();
    sh1 = (Shape) ob2;
    r1 = (Rectangle)ob2;
    sq1 = (Square) ob2;

    System.out.println("Finished Class Operations that work with a cast");

    System.out.println("The next operation compiles, but blows up at runtime");

    Circle c1;
    c1 = (Circle)ob2;

  }
}


instanceof

In the above example, how do I deal with the fact that a cast operation can blow up?

Approach 1, is to surround the cast operation with try ... catch logic and catch the exception:
ClasscastException.
We will cover exception handling techniques later.

Approach 2, is to use the instanceof operator to determine when a cast is possible.

import exercise.*;
class Test
{
  public static void main (String[] args)
  {
    Square sq1 ;
    Rectangle r1;
    Object ob1;

    Object ob2 = new Square();
    if (ob2 instanceof Rectangle)
        r1 = (Rectangle)ob2;
      else
        System.out.println("ob2 is not an instanceof Rectangle");

    if (ob2 instanceof Square)
        sq1 = (Square) ob2;
     else
        System.out.println("ob2 is not an instanceof Square");


    Circle c1;
    if (ob2 instanceof Circle)
        c1 = (Circle)ob2;
      else
        System.out.println("ob2 is not an instanceof Circle");

  }
}


Impossible instanceof comparisons generate Compiler errors

The instanceof operator will only compile if it is possible for the object to actually be an instance of the referenced class. If your code compiles, then a runtime the instanceof operator will make the actual determination.

On the otherhand if it can be determined at compile time that there is no possibility, then a Compilation error will occur. For example, the following will generate a Compilation error:


import exercise.*;
class Test
{
  public static void main (String[] args)
  {
    Rectangle r1 = new Square();
    Circle c1;
    if (r1 instanceof Circle)// impossible --> Compile error
        System.out.println("r1 is an instanceof Circle");
      else
        System.out.println("r1 is not an instanceof Circle");
  }
}


Last Updated: January 21, 2003 10:47 AM