11 Replies Latest reply on Oct 8, 2007 12:35 AM by 807605

    Casting primitive wrap type variable to primitive

    807605
      We have this code:
      ...
      float f = 22.2f;
      System.out.println((int)f);
      ...
      Which prints out "22".
      ...
      Float f = 22.2f;
      System.out.println((int)f);
      ...
      When i changed the variabile type from float to Float, code throw up a compile a error, that Float type cannot be casted to int. Can somebody tell me what's going on? Isnt compiler supposed to do unboxing the variable to primitive float and then primitive float to be casted to an int?

      System.out.println((int)f.floatValue());
      Isnt that what the compiler is supposed to do? Code which works perfectly fine.

      Thank for your help.
        • 2. Re: Casting primitive wrap type variable to primitive
          807605
          There are limits to boxing and unboxing. At it's root, your f variable is a Float object, and you just can't cast an object that way.

          Try:
                  Float f = 22.2f;
                  System.out.println((int)f.floatValue());
          Also, when posting your code, please use code tags so that your code will be well-formatted and readable. To do this, place the tag [code] at the top of your block of code and the tag [/code] at the bottom, like so:
          [code]
            // your code block goes here.
          [/code]
          • 3. Re: Casting primitive wrap type variable to primitive
            807605
            Thank you for your reply.

            Yes, i know how to fix that error.
            I just want to know why it doesnt work and how the compiler do the cast in this case.
            Doesnt make sense why the compiler let you unbox followed by a widening cast, and doesnt let you narrow.
            Float f = 22.2f;
            System.out.println((double)f);
            Integer i = 22;
            System.out.println((long)i);
            Both compile with no error.
            There must be a second rule which adds these constrains, but i dont know what it is.
            • 4. Re: Casting primitive wrap type variable to primitive
              807605
              Please see this as it may help:

              http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4995668
              • 5. Re: Casting primitive wrap type variable to primitive
                807605
                Hello vcraescu,

                I think you are wondering why the compiler does not convert from Float to float (unboxing) and subsequently from float to short (your cast), so the bug report probably does not help you too much.

                Casts come into play when the compiler can not assure that the statement is semantically correct. The bytecode would be the same with or without the cast (if the compiler would accept omitting casts).

                Casting primitives:
                int i = 0;
                short s = (short)i;
                Purpose of the cast: The compiler has no idea whether the two higher bytes are relevant and you therefore need to specify that they are not.

                Casting reference types:
                With reference types there are two types of casts, the upcast and the downcast.
                class Exampe {
                     public static void example(Object... objects) {
                          for (Object object : objects) {
                               System.out.print(object);
                               System.out.print(' ');
                          }
                          System.out.println();
                     }
                
                     public static void main(String[] args) {
                          Object[] objects = { "String", 123, 1.23 };
                          example(objects);
                          example((Object) objects);
                     }
                }
                The purpose of this (up)cast is that you want the object to be treated as beeing of a more general type.
                org.w3c.dom.Node node;
                switch(node.getNodeType()) {
                     case org.w3c.dom.Node.ELEMENT_NODE:
                          org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                          // ...
                     case org.w3c.dom.Node.TEXT_NODE:
                          org.w3c.dom.Text element = (org.w3c.dom.Text) node;
                          // ...
                     default:
                          // ...
                }
                These casts tell the compiler that you are certain the object node is of a more specific type.

                My point is that all these casts have a distinct purpose. Your cast on the other hand does not. Looking at your code I see a conversion from a wrapper to a primitive, not necessarily the loss of precision connected with it and that is what this cast should be about.

                With kind regards
                Ben Schulz
                • 6. Re: Casting primitive wrap type variable to primitive
                  807605
                  Thank you for your reply Ben.

                  What you say is true, but doesnt make sense in our case.
                  If (double)Integer wasnt possible, i would have not come up with this question.
                  The bug report says: +"Consequently, there is no unboxing conversion from Long to int. This is+
                  +a good thing as it can cause loss of precision.+
                  +"+

                  Where does it say there is an unboxing conversion from Integer to double? but compiler let you do that.

                  The compiler only know that a wrap type can be unboxed and casted to a wider primitive type to prevent loss of data, but it would be good to state that on JLS.
                  • 7. Re: Casting primitive wrap type variable to primitive
                    807605
                    I just realized that both my reply and the bug report mention "loss of precision" as the reason for requiring a cast. The correct reason ^(1)^ is that the exact numeric value might be lost. According to the JLS ^(1)^ a loss of precision is acceptable, since merely the least significant bits are lost.

                    Sorry for the confusion.

                    With kind regards
                    Ben Schulz

                    -----
                    ^1^: http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.2
                    • 8. Re: Casting primitive wrap type variable to primitive
                      807605
                      BenSchulz wrote:
                      ...a loss of precision is acceptable, since merely the least significant bits are lost.
                      What do you mean by "acceptable"? The compiler won't permit an automatic conversion that will result in a loss of precision, any more than it will permit one that reduces the magnitude of the number.

                      I think the best answer to the original question is "because autoboxing was added to the language as an afterthought". This particualr issue is just a very minor annoyance, but autoboxing has created the potential for serious bugs, too. It's now possible to accidentally compare two integer values with == and get a false result, even though the values are the same. This can happen, for example, if one of the values is an Integer that was created by means of the {color:#000080}new{color} keyword instead of the valueOf() method or by autoboxing. If autoboxing had been part of the language from the beginning, it probably wouldn't have been possible to compare two Objects using ==, and we wouldn't have to constantly tell newbies to compare Strings with equals() instead. Just the thought of it brings tears to my eyes. (Or maybe it's the cat in my lap who did that: the new cat food doesn't seem to agree with him.)
                      • 9. Re: Casting primitive wrap type variable to primitive
                        807605
                        That may result in a loss of precision, but that's an explicit cast which should be allowed as it is for primitives.
                        I can live without narrowing a wrap type trough an explicit cast. It's just annoying.
                        But if Java designer want that, then all we can do is to comply with.

                        Thank you guys for answering to my question.
                        • 10. Re: Casting primitive wrap type variable to primitive
                          807605
                          uncle_alice wrote:
                          BenSchulz wrote:
                          ...a loss of precision is acceptable, since merely the least significant bits are lost.
                          What do you mean by "acceptable"? The compiler won't permit an automatic conversion that will result in a loss of precision, any more than it will permit one that reduces the magnitude of the number.
                          I'm not sure if we understand each other, but does the JLS ^(1)^ not explicitly say the opposite?
                          JLS 5.1.2 Widening Primitive Conversion
                          Here is an example of a widening conversion that loses precision:
                          class Test {
                               public static void main(String[] args) {
                                    int big = 1234567890;
                                    float approx = big;
                                    System.out.println(big - (int)approx);
                               }
                          }
                          which prints:

                               -46
                          Also comparing two Integers with the == operator has no guarantee of returning true, even when both we're created using valueOf(), except for values within byte range ^(2)^ (-128 to 127, inclusive).
                          class Test {
                               public static void main(String[] args) {
                                    for (int i = -129; i <= 128; ++i) {
                                         System.out.println((Integer) i == (Integer) i);
                                    }
                               }
                          }
                          With kind regards
                          Ben Schulz

                          -----

                          ^1^: http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.2
                          ^2^: http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7
                          • 11. Re: Casting primitive wrap type variable to primitive
                            807605
                            You're right, that is what it says. I was going to say that it shouldn't be possible, but when it comes to floating-point numbers, precision is pretty much a joke anyway. I still don't think it's "acceptable", but this particular unacceptable situation has existed since before I was born, so I'll let it pass. :-) Anyway, as the example shows, it only really becomes an issue when you convert the float back to an int, and that's a narrowing conversion, requiring an explicit cast.

                            As for comparing Integers with ==, yeah, I was talking about values within the magic range. Even within that range, you can make the comparison fail:
                            System.out.println((Integer) 3 == new Integer(3)); // false
                            But the real problem, as I see it, is that we can now, thanks to autoboxing, be dealing with wrapper objects without realizing it. That means we can't really know if == is comparing values or references. The fact that it sometimes acts like a value comparison when the operands are really references just makes it worse. I don't really see this as a problem for long-time Java programmers, but newbies won't necessarily be aware of this potential gotcha. I've even seen posts here by people who didn't seem to know the difference between primitives and wrapper objects - and it's now possible for professional programmers to get away with that brand of ignorance for years.