The SCJP Tip Line
Implicit Conversions, Explicitly
by Corey McGlone

In this article, I'm going to go over the basics of explicit and implicit conversions and some of the nuances associated with them. In general, the material in this article would be considered "fair game" for the SCJP exam so, if you're planning on taking the exam soon, you'll want to make sure you understand the concepts in this article well.

Most of you probably already know what a conversion is. In general terms, a conversion is the process of transforming one thing into another, such as melting ice to turn it into water. In Java, when we talk about conversions, we are generally talking about the process of changing the type of an object from one type to another. One example might be the process of converting an int to a long or a short to a byte. You can also convert objects from one type to another. Often, you'll hear such a conversion called a "cast." For example, to turn an int into a byte, you "cast" the int as a byte.

Widening (Safe) Conversions vs. Narrowing (Unsafe) Conversions

There are two basic types of conversions: widening and narrowing. You might also hear these referred to as safe and unsafe conversions, respectively.

So, what is a widening conversion, and why is it safe? Well, when dealing with primitives, a widening conversion occurs when you convert one type, A, to another type, B, when type B has a larger (or wider) range than A. This is the type of cast that occurs when you cast a byte to an int. A byte has a range of just 256 values, from -128 to 127. An int, on the other hand, has a range of 4,294,967,296 values, ranging from -2,147,483,648 to 2,147,483,647. Obviously, any value that fits in a byte will fit in an int. Because of this, there is no chance for data loss and the conversion is considered "safe."

With that in mind, can you guess what a narrowing conversion is and why it is considered unsafe? Based on our previous conclusion, that a widening conversion was considered "safe" because there was no chance for data loss, it seems only natural that a narrowing conversion would be considered "unsafe" because there is a chance for data loss. If we turn our previous example around and try to cast an int as a byte, we have a narrowing conversion. This is because the range of a byte is much smaller (or narrower) than the range of an int. What happens if you try to convert an int with a value of 200 to a byte? You're bound to lose some data because the number 200 is not within the range of a byte. Performing such a conversion will change the number 200 into -56 because data is lost.

If you'd like to get more details about widening and narrowing conversions, I'd suggest checking out this article in the SCJP Tip Line.

Implicit vs. Explicit Conversions

I know, I know - I just stated that there are two types of conversions and now I've gone and come up with two more types. Well, in my defense, implicit and explicit conversions aren't really new conversions, they simply describe how narrowing and widening conversions are performed. An implicit conversion is performed automatically, with no additional input from you (the programmer). An explicit conversion, on the other hand, is not performed automatically and is, instead, dictated by you, the programmer.

I'm sure it comes as no shock to you that widening conversions, which are always safe from data loss, generally occur implicitly, while narrowing conversions, which run the risk of data loss, are usually performed explicitly. Let's take a look at an example that performs both:

Source Code
            
public class Conversions
{
    public static void main(String[] args)
    {
        int i = 200;
        byte b = 10;
        
        int j = b;              // 1
        byte c = (byte)i;       // 2
        
        System.out.println(j);
        System.out.println(c);
    }
}

// Output

10
-56
            

Take a look at line 1. Notice that we're assigning a byte variable to an int variable. Java is a strongly typed language so you can't just assign one data type to another. Therefore, before this is allowed, some sort of conversion much take place. In this case, we're going from a byte to an int, which is a widening conversion. The compiler realizes that and simply adds a cast to convert the byte to an int prior to assignment. This is called an implicit cast because Java does it for you. Pretty convenient, huh? (See §5.2 Assignment Conversion in the JLS for more details about assignment conversions.)

Now let's take a look at line 2. In this case, we're trying to assign an int to a byte variable. We already know from our previous discussion that this is considered a narrowing conversion and we risk losing data if we do so. Because of this, the compiler won't just do the conversion for us. Rather, we need to tell the compiler, explicitly, that we want the conversion to take place. We do that by adding the (byte) in front of the variable. This is your way of telling the compiler, "Yes, I know this is risky, but I'll take the responsibility for it. Just do the conversion." Without that directive, the compiler will give you an error about that line.

Implications of Implicit Casting

In general, explicit casts are easy to deal with because you're the one making them. You have to know what type something is, because you're the one telling the compiler which type to make it. However, because the compiler will sometimes cast things for you, you might not realize that it's doing such a thing and the results can be... unexpected. Let's look at a few examples.

Binary Numeric Promotion

Let's start in the wonderful world of computer hardware. Imagine you're creating a simulator that is designed to test a piece of hardware that your company is designing. In order to test the software, you need to read in two bytes of data from the hardware and xor those bytes together. You'll be storing the result in another byte variable for later use. Here's a little snippet of code that you might be using for such an application:

Source Code
            
public class HardwareTester
{
    public static void main(String[] args)
    {
        byte input1 = readByteFromHardware();
        byte input2 = readByteFromHardware();
        
        byte xorTotal = input1 ^ input2;
        
        // Do some more processing using xorTotal
    }
    
    private static byte readByteFromHardware()
    {
        // Reads a byte of data from the hardware and returns it.
        
        // For the sake of this example, assume this method
        // is complete and returns a value.
    }
}
            

Looks like a simple program, right? Well, the bad news is that this program doesn't even compile! Why not? Can you see the error? The compiler message helps to give it away - here's what it says:

HardwareTest.java:8: possible loss of precision
found   : int
required: byte
        byte xorTotal = input1 ^ input2;
                               ^
So, the question is, why is the compiler complaining about a narrowing conversion at this point? Notice that it says you're trying to convert an int to a byte. Certainly, converting an int to a byte is a narrowing conversion, but where the heck did the int come from? There isn't an int anywhere in that code!

Well, the int shows up because an implicit conversion is taking place. In Java, any time you perform a binary operation (an operation requiring two operands), Binary Numeric Promotion is performed. Binary Numeric Promotion casts each operand up to the size of the other or, if neither is larger than an int, both are cast as ints. What type do you think you'll get as a result of xor'ing two int values together? You're going to get an int, of course. That's why you're getting a compiler error - the xor operation is returning an int and we know that we can't assign an int to a byte without a cast because that's a narrowing conversion. Therefore, in order to make the above code work, you must add a cast to the xor statement, like this: byte xorTotal = (byte)(input1 ^ input2);

Implicit Explicit Casts

Next, let's focus on implicit explicit casts. What?!? Yeah, you heard me - implicit explicit casts. There is a case in which an explicit cast is implied. Anyone know what it is?

The answer is a compound assignment operator. Compound assignment operators, such as +=, -=, *= all contain an explicit cast, even though it's not shown. Take the following application as an example:

Source Code
            
public class ImplicitExplicitCast
{
    public static void main(String[] args)
    {
        byte b1 = -13;
        b1 >>>= 1;
        
        System.out.println(b1);
    }
}
            

What does this program print? Even if you don't know what the value of -13 shifted to the right one position is off the top of your head, you can be certain that the result will be positive because we're using the unsigned right shift operator. However, if that's your assumption, I'm afraid to say you need to stop making assumptions. This program prints -7.

So what went wrong? Unlike our hardware program, there was no compiler error to tell us that something wasn't right. The compiler was just fine with the code we wrote but, if the code is good, why the failure?

Well, the reason for the "failure" is that compound assignment operators contain an explicit cast, implicitly. :) If I rewrite this code snippet and expand the compound assignment operator to what it truly is, the code would look like this:

Source Code
            
public class ImplicitExplicitCast
{
    public static void main(String[] args)
    {
        byte b1 = -13;
        b1 = (byte)(b1 >>> 1);
        
        System.out.println(b1);
    }
}
            

From that, the problem is probably a bit more obvious. From our previous conversation about binary numeric promotion, we know that b1 is first going to be cast from an 8 bit byte to a 32 bit int. Next, we shift all of the bits 1 position to the right and add a 0 to the left. That makes the resultant value positive, right? Well, yes, it does...for a moment. The very next thing we do, though, is cast our int as a byte and chop off the high order 24 bits, which includes our precious sign bit. What we're left with is -7.

Those are just a couple cases in which you can get into trouble from an implicit cast. I'm sure there are plenty more "gotchas" out there waiting for you so be wary. Check out Dan Chisholm's mock exams (referenced below) for some additional practice.

Conclusion

Understanding type conversions is very important with regards to the SCJP exam. You might not see these exact examples on your exam, but the principles are certainly fair game. Make sure you understand this material well. Besides the SCJP exam, understanding conversions and their implications is a fundamental topic that should be well understood by any Java programmer.

Always be wary of picky details when you're taking the exam but be especially wary if you start seeing data types like bytes and shorts. If those appear in a question, make sure there aren't any hidden conversions you're missing. Below, I've cited some great resources for more information on this topic if you need some more clarification. Of course, if this material is giving you trouble, feel free to pop into the SCJP Forum.

JLS: Conversions and Promotions
SCJP Tip Line: Widening and Narrowing Conversions
JLS: Compound Assignment Operators
Dan Chisholm's Mock Exams, Including Single-Topic Exams on Conversions

Until next time,
Corey