Home of The JavaSpecialists' Newsletter

245Surprising += Cast

Posted: 2017-01-30Category: LanguageJava Version: AllDr. Heinz M. Kabutz
 

Abstract: Surprisingly, the compound arithmetic expression contains a cast that can produce some interesting side effects. In this newsletter we explore this and other edge cases in the Java Language Specification.

 

Welcome to the 245th edition of The Java(tm) Specialists' Newsletter. I mentioned last month that I go running every day. Not fast and not far, but every day. A lot of Java programmers have told me that I had inspired them to exercise more. Our jobs make us sit on our backsides all day long. As a geek, I find that the more data about my exericse I gather, the more it inspires me. Heart beat, temperature, left-right balance. I can then track my progress over the months. You don't need an expensive watch. There are many apps you can run on your phone to help you. The one I use is Endomondo. But others do an equally good job. The important thing is to be active every day. You will thank me 50 years from now :-)

Last Thursday I spoke at the Java User Group in Darmstadt about how ManagedBlocker can turbo charge your CPU utilization when using Fork/Join. It was well received with lots of interesting questions and discussions. I am doing the talk again on the 2nd February 2017 at Heinz's Happy Hour. You will learn how ManagedBlocker works and how you can hack Fork/Join logic into existing code to get better performance. We have live discussions in our Slack Team in the heinzs-happy-hour channel.

NEW: Refactoring to Java 8 Streams and Lambdas Workshop Are you currently using Java 6 or 7 and would like to see how Java 8 can improve your code base? Are you tired of courses that teach you a whole bunch of techniques that you cannot apply in your world? Check out our one day intensive Refactoring to Java 8 Streams and Lambdas Workshop.

Surprising += Cast

'Twas the year 1999. Martin Fowler was coming to town. Cape Town, that is. I had no idea who he was, only that some Java event was happening. In my city! I had to be there! So I arrived, all decked out, wearing my gleaming "Sun Certified Java Programmer" badge. Perhaps I'd meet rich companies looking for Java programmers?

Martin Fowler spoke about something called "refactoring". I looked it up in the dictionary and came back empty. Microsoft Word marked it as a spelling error. But it sounded interesting, so I decided to take a look. I sat in front. In those days, my eyesight was a lot better than it is now. I do have glasses, which I bought especially for Java conferences. But I don't like wearing them. So my seat is usually in front. Call me a keen bean.

First impressions were just "wow". I had never before heard anyone as loud as Martin. He would make a perfect ship's captain, bellowing orders as another hurricane was bearing down on his vessel. What he said, or rather thundered, about "refactoring" resonated with me. Unit tests to check the basic functionality and automatic tools to do the heavy lifting? Refactorings that were scientifically proven to alter the structure of the source code, yet still give the same results? Sounded like magic!

(Whilst searching for the Refactoring book on Amazon, they reminded me "You purchased this item on July 8, 1999." My copy is from the first printing, June 1999 - a collector's item by now.)

Two things stood out to me during his talk (besides Martin's built-in megaphone). First off, his Visual Age IDE crashed badly, taking the entire project with it. He had to do a restore whilst muttering that this "wasn't very nice". Secondly, in one of his refactorings he extracted a method. He "accidentally" changed the return type from double to int. Amazingly, the code still compiled, because he was using the result in a += calculation. It went something like this:

// Extract from Refactoring by Martin Fowler
double thisAmount = 0;
Rental each = (Rental) rentals.nextElement();
//determine amounts for each line
switch (each.getMovie().getPriceCode()) {
  case Movie.REGULAR:
    thisAmount += 2;
    if (each.getDaysRented() > 2)
      thisAmount += (each.getDaysRented() - 2) * 1.5;
    break;
  case Movie.NEW_RELEASE:
    thisAmount += each.getDaysRented() * 3;
    break;
  case Movie.CHILDRENS:
    thisAmount += 1.5;
    if (each.getDaysRented() > 3)
      thisAmount += (each.getDaysRented() - 3) * 1.5;
    break;
}
  

The code smells, so he extracted the switch statement manually:

double thisAmount = 0;
Rental each = (Rental) rentals.nextElement();
thisAmount = amountFor(each);

private int amountFor(Rental each) {
  int thisAmount = 0;
  switch (each.getMovie().getPriceCode()) {
    case Movie.REGULAR:
      thisAmount += 2;
      if (each.getDaysRented() > 2)
      thisAmount += (each.getDaysRented() - 2) * 1.5;
      break;
    case Movie.NEW_RELEASE:
      thisAmount += each.getDaysRented() * 3;
      break;
    case Movie.CHILDRENS:
      thisAmount += 1.5;
      if (each.getDaysRented() > 3)
      thisAmount += (each.getDaysRented() - 3) * 1.5;
      break;
  }
  return thisAmount;
}
  

If you look at the code above, the amountFor() method should have returned a double. But when Martin Fowler refactored it, he used an int as a return type.

Two things show us how old this book is. First off, he is talking about a computerised billing system for a "video store". In the days before Netflix, we used to drive to a shop that had hundreds of videos. These were like DVDs, but on magnetic tape. There were no chapters to jump to. The quality was analog and we never knew if the tape had been damaged. We used to pay to rent videos for a night and then had to return them the next day. If we forgot or missed the cut-off time, we'd be billed for an extra day. Some stores even penalized us if we didn't rewind the videos. Those were dark days.

Secondly, when Martin extracted the code, I thought it would not compile. I expected javac to complain about him calling thisAmount += 1.5; without an explicit cast. In my mind, I thought that this was equivalent to thisAmount = thisAmount + 1.5; However, it is rather thisAmount = (int)(thisAmount + 1.5); That's right, javac adds an explicit cast when we use a compound operation in Java. It is described in the Java Language Specification section 15.26.2 "Compound Assignment Operators":

"A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once."

Last week, Dr Fatma C. Serce sent me an email outlining three cases that her student Chao Tan had shown her. It reminded me of this little factoid. Here are their cases:

public class CastSurprise {
  public static void main(String... args) {
    // case 1:
    char c1 = 'a' + 5;  // compiles

    // case 2:
    char c2 = 'a';
    c2 = c2 + 5; // does not compile

    // case 3:
    char c3 = 'a';
    c3 += 5; // compiles
  }
}
  

In case 1, javac could determine at static compile time that the value would not lose precision. If instead of 5 we add 500_000, then javac would complain, because we would overflow the 16 bit char type.

In case 2, since we are increasing c2 without an explicit cast, javac cannot guarantee that the result would fit inside 16 bits. It thus forces us to add a cast.

In case 3, the cast to char is added by the compiler. As we saw above, the statement c3 += 5; is equivalent to c3 = (char)(c3 + 5);

Mental Gymnastics - Left-Hand Operands

Onto something else, mildly related. To test how well you understand Java, here are three code snippets. Your job is to guess the output without running them. In C it wouldn't necessarily be well-defined. In Java you can find the definition in JLS 15.7.1. However, please try figure it out before running them:

public class Test1 { // JLS 15.7.1
  public static void main(String... args) {
    int i = 2;
    int j = (i = 3) * i;
    System.out.println(j);
  }
}
  

And now for the second one:

public class Test2 {// JLS 15.7.1
  public static void main(String... args) {
    int a = 9;
    a += (a = 3); // first example
    System.out.println(a);
    int b = 9;
    b = b + (b = 3); // second example
    System.out.println(b);
  }
}
  

And our last one:

public class Test3 {// JLS 15.7.1
  public static void main(String... args) {
    int j = 1;
    try {
      int i = forgetIt() / (j = 2);
    } catch (Exception e) {
      System.out.println("Now j = " + j);
    }
  }

  static int forgetIt() throws Exception {
    throw new Exception("I'm outta here!");
  }
}
  

I hope you enjoyed these and that you'll try them out.

Last week I presented my Extreme Java - Advanced Topics course in Germany, now available as self-study on Vimeo.

We've launched a new product: A one day Refactoring to Java 8 Lambdas and Streams Workshop. We tried it last week. It worked even better than I had expected it to. Feedback from the client: @heinzkabutz you did a very good job! It was a lot of fun seeing our code morphing to the advantages of Java8. Learned a lot! Thanks!

Kind regards

Heinz

P.S. For the lazy amongst us, here are the answers: Test1 outputs 9, Test2 outputs 12 twice, Test 3 outputs "Now j = 1"

 

Related Articles

Browse the Newsletter Archive

About the Author

demo

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

Nobody ever wants to call a Java performance consultant, but with first-hand experience repairing and improving commercial Java applications - JavaSpecialists are a good place to start...

Threading Emergency?

If your system is down, we will review it for 15 minutes and give you our findings for just 1 € without any obligation.