Autoboxing



  • Java has a feature "autoboxing" that implicitly converts from a native int to an immutable Integer object. Ditto for short->Short, long->Long, etc.

    Our "star" junior developer committed this:

        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    // populate it
    ...
    long theKey = someObject.getTheKeyValue();
    Integer theVal = map.get(theKey);

    ...and couldn't understand why theVal was always null. For those who don't know Java, it will implicitly convert theKey from long to Long, which is compared by the equals() method in the base class: Object, which does a reference compare (by address). Since Integer(123) will NEVER equal Long(123), the lookup will always fail. Thus, we have his next checkin:

        // Don't know why lookups on this map always fail
    //Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    Map<String,Integer> map = new HashMap<String,Integer>();
    // populate it
    ...
    long theKey = someObject.getTheKeyValue();
    //Integer theVal = map.get(theKey);
    Integer theVal = map.get(""+theKey);


  • Holy fucksocks...



    How much is this guy getting paid?



  • @eViLegion said:

    Holy fucksocks...

    How much is this guy getting paid?


    Snoofle did say that the guy is a junior dev.



  • @snoofle said:

    For those who don't know Java, it will implicitly convert theKey from long to Long, which is compared by the equals() method in the base class: Object, which does a reference compare (by address). Since Integer(123) will NEVER equal Long(123), the lookup will always fail.

    Wow. I would have been bitten by this too. Why doesn't the boxed Integer class override .Equals()? WTF Oracle.

    Thank God for C#.



  •  @eViLegion said:

    How much is this guy getting paid?

    How much was the guy that tought "2 == 2L should be false, isn't that obvious?"  getting paid?



  • DISCLAIMER: The junior developer is still part of the WTF assuming that Java is his primary programming language and that he isn't still taking courses in Java. 

    The real WTF is Java in this case.
    That is a really poor implementation of equals.
    I hate Oracle, but if I have a VARCHAR (string) field in oracle with a value of "123" and perform a search on that field for the numeric value 123 even ORACLE is smart enough to realize that the two are equivalent and return the correct value.

    While I can understand certain edge case scenarios where you would want to ensure that the operands of a numeric operation were of the same scope (Int/Int ; Long/Long) in order to assure that the precision of an operation performed was accurately scoped, that should at best be a completely different comparison from =; perhaps a Object1.TypeOf = Object2.TypeOf comparison.  But based on common human convention, when using an = we want to compare just the values.

    I can't count the number of times I have seen high level programming languages throw thousands of years of human experience down the drain so that some developer can either (A) explain a their inability to develop a solution or (B) promote some impractical ideal of an operation.

    Which is it in this case?  Did someone support the idea that Int 123 cannot be the same as Long 123?  Or could they not figure out how to get the comparison operator to work on the actual value (convert the values to the higher precision object type prior to the compare)?



  • Hold on, just re-read the example code.  The practical issue is that Java can't downconvert a Long to an Integer, even if the Long is in scope of Int.  But it will apparently most happily convert a string to an Integer? 



  • The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.



    The WTF is not even that some douchebag created a map with a key type of TypeA, and a value type TypeB, then trying to access that with a key of TypeC.

    Yeah, it is obviously a stupid thing to do, regardless of the similarity between TypeA and TypeB, and I'm sure we've all done it before at some point, briefly.



    No, TRWTF is that having done that, he was unable to correct the error immediately, and instead DID IT AGAIN! This time, however, he managed to tard it up even more than before, with TypeA and TypeC being completely dissimilar, and requiring a concat operation to convert.




    Is he really considered a "star" developer at your office?



  • @Medezark said:

    I hate Oracle, but if I have a VARCHAR (string) field in oracle with a value of "123" and perform a search on that field for the numeric value 123 even ORACLE is smart enough to realize that the two are equivalent and return the correct value.

    Err, comparing a string to a number shouldn't be considered equivalent at all. Weak typing/automatic type coercion is a dangerous game.



  • @eViLegion said:

    Is he really considered a "star" developer at your office?
    He is considered a star JUNIOR developer, but, sadly, still one of the better ones here.



  • Hmmm, I misspoke (doing two things at once).

    While Object.equals() does an Object-address compare, the immutables have their own implementation of equals (which overrides Object.equals). In it, they always return false for different types, and do a mathematical comparison for the same object type. Thus, Integer vs Long are different types and will never match. Integer vs Integer will cause a mathematical comparison to be done.

    Still, it would have made more sense (IMHO) to always do the mathematical comparison internally (at least for two numeric values).



  • @snoofle said:

        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    // populate it
    ...
    long theKey = someObject.getTheKeyValue();
    Integer theVal = map.get(theKey);

    And he didn't see the "long" being used here -- where Integer should have been used. And no one doing a code review caught this? (you are doing code reviews, right?) And the junior developer didn't understand that when something is not working right, that should be, he should ask someone to take a look at the code? At least 3 WTF's here.


  • @snoofle said:

    While Object.equals() does an Object-address compare, the immutables have their own implementation of equals (which overrides Object.equals). In it, they always return false for different types, and do a mathematical comparison for the same object type. Thus, Integer vs Long are different types and will never match. Integer vs Integer will cause a mathematical comparison to be done.

    That makes... slightly more sense. But thankfully, C# isn't that dumb.

    Int32 testInt = 29;
    Int64 testInt2 = 29;


    if (testInt == testInt2)

    {

    Console.WriteLine("yay");

    }



  • @eViLegion said:

    The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.

    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.



  • @DrPepper said:

    @snoofle said:

        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    // populate it
    ...
    long theKey = someObject.getTheKeyValue();
    Integer theVal = map.get(theKey);

     

    And he didn't see the "long" being used here -- where Integer should have been used. And no one doing a code review caught this? (you are doing code reviews, right?) And the junior developer didn't understand that when something is not working right, that should be, he should ask someone to take a look at the code? At least 3 WTF's here.
    To be fair, this is a condensed example. The original code was spread out over ~60 lines, but still, I can't see any good reason why the designers of Auto Boxing didn't design it to auto-upconvert and just do the mathematical comparison.

    It's one of those syntactic-sugar features that sometimes causes more problems than it solves.

    ...And I've probably mentioned hundreds of times that nobody here except me does code reviews, and I've been pulled off of that for reasons to be explained in an upcoming front page post.

  • Considered Harmful

    @blakeyrat said:

    @snoofle said:
    While Object.equals() does an Object-address compare, the immutables have their own implementation of equals (which overrides Object.equals). In it, they always return false for different types, and do a mathematical comparison for the same object type. Thus, Integer vs Long are different types and will never match. Integer vs Integer will cause a mathematical comparison to be done.

    That makes... slightly more sense. But thankfully, C# isn't that dumb.

    Int32 testInt = 29;
    Int64 testInt2 = 29;


    if (testInt == testInt2)

    {

    Console.WriteLine("yay");

    }


    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    if (testInt != testInt2)
    {
    Console.WriteLine("poopity poo");
    }

    Which is what is happening in the Java sample as well.


  •  @joe.edwards said:

    @blakeyrat said:
    @snoofle said:
    While Object.equals() does an Object-address compare, the immutables have their own implementation of equals (which overrides Object.equals). In it, they always return false for different types, and do a mathematical comparison for the same object type. Thus, Integer vs Long are different types and will never match. Integer vs Integer will cause a mathematical comparison to be done.

    That makes... slightly more sense. But thankfully, C# isn't that dumb.

    Int32 testInt = 29;
    Int64 testInt2 = 29;


    if (testInt == testInt2)

    {

    Console.WriteLine("yay");

    }


    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    if (testInt != testInt2)
    {
    Console.WriteLine("poopity poo");
    }

    Which is what is happening in the Java sample as well.
    Maybe we need to round up and shoot maim hurt the language designers?


  • @blakeyrat said:

    @snoofle said:
    While Object.equals() does an Object-address compare, the immutables have their own implementation of equals (which overrides Object.equals). In it, they always return false for different types, and do a mathematical comparison for the same object type. Thus, Integer vs Long are different types and will never match. Integer vs Integer will cause a mathematical comparison to be done.

    That makes... slightly more sense. But thankfully, C# isn't that dumb.

    Int32 testInt = 29;
    Int64 testInt2 = 29;


    if (testInt == testInt2)

    {

    Console.WriteLine("yay");

    }


    That's not a like-for-like comparison. int vs long equality comparison works in the same way as your example. The part which mangles things is the conversion to wrapper objects because Java's primitives aren't objects, unlike .Net's.



  • @joe.edwards said:

    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    Ok prepare to call me the real WTF, but I thought Int32 was the boxed version of int and Int64 was the boxed version of long.

    What's the difference between C# Int32 and Java's Integer that the Java one is boxed and the C# one is not boxed?



  • @blakeyrat said:

    @joe.edwards said:
    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    Ok prepare to call me the real WTF, but I thought Int32 was the boxed version of int and Int64 was the boxed version of long.

    What's the difference between C# Int32 and Java's Integer that the Java one is boxed and the C# one is not boxed?

    Go doesn't have boxed numeric types, but you can make one if you REALLY want to.

    // NEVER FUCKING DO THIS
    type MyInt32 struct {
       Number int32
    }
    

  • Considered Harmful

    @blakeyrat said:

    @joe.edwards said:
    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    Ok prepare to call me the real WTF, but I thought Int32 was the boxed version of int and Int64 was the boxed version of long.

    What's the difference between C# Int32 and Java's Integer that the Java one is boxed and the C# one is not boxed?

    int is a keyword in C# that means directly System.Int32. long is a keyword in C# that means System.Int64. You'll get the exact same CIL from using either interchangeably. That said, I prefer the language keywords wherever they exist.

    Boxing (aka casting basic types to object) hasn't been very useful ever since generics were introduced in 2.0. C#, unlike Java, doesn't require boxing to do useful things with structs/basic data types.



  • Huh. Ignorance fought. Thanks.

    (Just to be a zealot) but the basic point: "I never have to worry about this particular quirk" still applies for C# since, as it turns out, I guess I've never really had to box anything ever. Not that I can recall.


  • Considered Harmful

    @blakeyrat said:

    Huh. Ignorance fought. Thanks.

    (Just to be a zealot) but the basic point: "I never have to worry about this particular quirk" still applies for C# since, as it turns out, I guess I've never really had to box anything ever. Not that I can recall.

    I cut my teeth on 1.1, where all the collection types were Objects. Ugh.

    It was pretty bad, and Microsoft *gasp* acknowledged it and improved the API vastly in the next version, even though it pissed off a lot of people because it broke backward-compatibility (except not really because you can run .NET Framework versions side-by-side). Short term pain, long term gain.



  • @snoofle said:

    @DrPepper said:

    @snoofle said:

        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    // populate it
    ...
    long theKey = someObject.getTheKeyValue();
    Integer theVal = map.get(theKey);

     

    And he didn't see the "long" being used here -- where Integer should have been used. And no one doing a code review caught this? (you are doing code reviews, right?) And the junior developer didn't understand that when something is not working right, that should be, he should ask someone to take a look at the code? At least 3 WTF's here.
    To be fair, this is a condensed example. The original code was spread out over ~60 lines, but still, I can't see any good reason why the designers of Auto Boxing didn't design it to auto-upconvert and just do the mathematical comparison.

    It's one of those syntactic-sugar features that sometimes causes more problems than it solves.

    ...And I've probably mentioned hundreds of times that nobody here except me does code reviews, and I've been pulled off of that for reasons to be explained in an upcoming front page post.
    Consider the following example:
    Double a = 1.0;
    Double b = 1.1;
    Integer c = 1;
    boolean d = a.equals(c);
    boolean e = b.equals(c);
    boolean f = a.equals(b);
    
    Should d, e or f evaluate true? There are other worse examples, but I consider the strong type safety of Java one of it's strong points. I'm not so sure about autoboxing though..

    The problem is imho that the Map interface has the method V get(Object key) instead of V get(K key). If Java had generics from the start, this would've been one less pitfall, but it still only something a junior Java developer should do.

    C# never had any momentum before 2.0, so for MS it was a small cost to break backwards compatibility. For Sun (please don't mention the O-word) it was more important with backwards compatibility. 10 years later it's easy to say that it makes it easier to do a mistake, but I still think they made the right one.

    This can't be considered a big issue anyway, since most static code analysis tools will find this one for you. A jenkins server that just compiles the java code and runs some of the open source code analysis tools can be run off any old desktop machine and used to email the developer in case you don't have a code review tool.

    Snoofle, I've worked for at least one company where code review was dismissed by the programming architect with "tried it once and didn't work". So, while I'm looking forward to your front page post from whatever wtfcorp has done this time, it's not that unusual for a company not to see any point in it.



  • @joe.edwards said:

    @blakeyrat said:
    Huh. Ignorance fought. Thanks.

    (Just to be a zealot) but the basic point: "I never have to worry about this particular quirk" still applies for C# since, as it turns out, I guess I've never really had to box anything ever. Not that I can recall.

    I cut my teeth on 1.1, where all the collection types were Objects. Ugh.

    It was annoying to have to put in the cast when doing things, but I kept this from biting me by using sys hungarian on all the collections so I remembered to put it in.  You really only had it bite you once, then found a way around it.



  • @pjt33 said:

    @eViLegion said:
    The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.
    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.
    In other words... TRWTF is Java. I get bitten by this all the time. I'm still hoping they change this in a future release but I'm not holding my breath.


  • Considered Harmful

    @cconroy said:

    @pjt33 said:

    @eViLegion said:
    The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.

    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.
    In other words... TRWTF is Java. I get bitten by this all the time. I'm still hoping they change this in a future release but I'm not holding my breath.


    What was the argument against fixing it? Except, you know, a general policy of keeping all bugs around forever.



  • @joe.edwards said:

    @cconroy said:

    @pjt33 said:

    @eViLegion said:
    The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.
    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.
    In other words... TRWTF is Java. I get bitten by this all the time. I'm still hoping they change this in a future release but I'm not holding my breath.

    What was the argument against fixing it? Except, you know, a general policy of keeping all bugs around forever.

    I would assume that at this point it is a matter of backward compatability.  If applications were written to take advantage of the type casting differences they would break once the equivalency issue were fixed.



  • @toface said:

    Consider the following example:
    Double a = 1.0;
    Double b = 1.1;
    Integer c = 1;
    boolean d = a.equals(c);
    boolean e = b.equals(c);
    boolean f = a.equals(b);

    Should d, e or f evaluate true? There are other worse examples, but I consider the strong type safety of Java one of it's strong points. I'm not so sure about autoboxing though..

    Here's fun one for you: what will the following code output?

    [code]
            Integer i1 = 128;
            Integer i2 = 128;
            Integer i3 = 129;
            Integer i4 = 129;
            
            System.out.println(i1 == i1);
            System.out.println(i3 == i4);
    [/code]
    


  • Parlor tricks. but it's supposed to be 127 or -128 or the trick shoudln't work.

    You can also have fun with reflections and change the value of Integer.valueOf(1) to something else.



  • @toface said:

    Parlor tricks. but it's supposed to be 127 or -128 or the trick shoudln't work.

    You can also have fun with reflections and change the value of Integer.valueOf(1) to something else.

    Derp. I did a typo. First comparison is comparing the same two objects. You're right, I'm wrong, bow to the parlor trick master.


  • Discourse touched me in a no-no place

    @toface said:

    The problem is imho that the Map interface has the method V get(Object key) instead of V get(K key). If Java had generics from the start, this would've been one less pitfall, but it still only something a junior Java developer should do.
    But if K was Number we'd be straight back where we are now, but now with official type correctness. The real problem is that the equality operations on both Integer and Long don't make the values the same. I usually consider any autoboxing to be a bit of a code smell, though there are places where it is totally necessary (e.g., method calls that take a variable number of arguments).



  • @dkf said:

    The real problem is that the equality operations on both Integer and Long don't make the values the same. I usually consider any autoboxing to be a bit of a code smell, though there are places where it is totally necessary (e.g., method calls that take a variable number of arguments).
     

    But having equality between Long and Integer would not be enough for the code to work. For this code to work work, it is also require that Long.hashCode() returns the same values as Integer.hashCode() would, when constructed with the same int. (That is: Long(n).hastCode()==Integer(n).hachCode). Which it does for the java implementation I currently use, but that is an implementation detail, not a documented feature you can relay on.

     

     

     



  • @blakeyrat said:

    Ok prepare to call me the real WTF, but I thought Int32 *was* the boxed version of int and Int64 *was* the boxed version of long.

    What's the difference between C# Int32 and Java's Integer that the Java one is boxed and the C# one is not boxed?

     

     C#'s Int32 is a struct- a value type. technically C#'s "int" is merely an alias to System.Int32.

    Basically, C# doesn't have special Boxed primitive types. The closest you can come is by placing primitive types into Object variables, which performs boxing and unboxing through casts.

     

    Of course C#'s generics also support value types and ref and out parameters so really the "Reference" types for each primitive are pretty much redundant.

     



  • @toface said:

    Consider the following example:
    Double a = 1.0;
    Double b = 1.1;
    Integer c = 1;
    boolean d = a.equals(c);
    boolean e = b.equals(c);
    boolean f = a.equals(b);
    

    Should d, e or f evaluate true? There are other worse examples, but I consider the strong type safety of Java one of it's strong points. I'm not so sure about autoboxing though..

    True, false, false in C#, like you would expect.  It casts the integer to a double to do the comparison. 

    @toface said:

    C# never had any momentum before 2.0, so for MS it was a small cost to break backwards compatibility.
     

    Citation needed@toface said:

    10 years later it's easy to say that it makes it easier to do a mistake, but I still think they made the right one.
    Well that makes 1@toface said:
    This can't be considered a big issue anyway, since most static code analysis tools will find this one for you.
    Yes, it can.  Not everyone uses static analysis tools, and it is obscure and obviously causes bugs.  The language CAUSES bugs.

    @blakeyrat said:

    Huh. Ignorance fought. Thanks.

    (Just to be a zealot) but the basic point: "I never have to worry about this particular quirk" still applies for C# since, as it turns out, I guess I've never really had to box anything ever. Not that I can recall.

     Interesting note - if you have a generic type and use it 100 times for classes, only 1 set of IL will be generated.  But each and every struct you put in it has to generate a new set of IL so that it doesn't have to constantly box/unbox them.

     

    Also, the problem here is really that Java doesn't automatically cast numbers to the larger type (like C#).

    @joe.edwards said:

    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    if (testInt != testInt2)
    {
    Console.WriteLine("poopity poo");
    }

    Which is what is happening in the Java sample as well.

     

    Except that ((long)29).Equals((int)29) is true in C#, which I don't believe Java does.  Also, C# has a way of working around this, by having real generics.



  • @Ben L. said:

    Go doesn't have boxed numeric types, but you can make one if you REALLY want to.

    // NEVER FUCKING DO THIS
    type MyInt32 struct {
       Number int32
    }

    Except you fucking have to do this whenever you want to use any other collection than the built in array or hash map, because Go chose to go down the same highway to hell as Java and not include generics. They are in for the same set of WTFs as Java and a few of their own.



  • @Bulb said:

    @Ben L. said:
    Go doesn't have boxed numeric types, but you can make one if you REALLY want to.

    // NEVER FUCKING DO THIS
    type MyInt32 struct {
       Number int32
    }

    Except you fucking have to do this whenever you want to use any other collection than the built in array or hash map, because Go chose to go down the same highway to hell as Java and not include generics. They are in for the same set of WTFs as Java and a few of their own.

    We have interfaces for that shit. If your program requires the use of primitives in a data structure but you don't know what primitives it'll use, YOU are TRWTF.


  • @Sutherlands said:

    @joe.edwards said:

    It is if you box them.

    object testInt = (Int32)29;
    object testInt2 = (Int64)29;

    if (testInt != testInt2)
    {
    Console.WriteLine("poopity poo");
    }

    Which is what is happening in the Java sample as well.

     

    Except that ((long)29).Equals((int)29) is true in C#, which I don't believe Java does.  Also, C# has a way of working around this, by having real generics.

     

    I just tested in C#, it's even worse than I thought:

                Console.WriteLine("== on unboxed integers: {0}", ((int)29) == ((long)29));
                Console.WriteLine("Equals() on unboxed integers 1: {0}", ((int)29).Equals((long)29));
                Console.WriteLine("Equals() on unboxed integers 2: {0}", ((long)29).Equals((int)29));
                Console.WriteLine("== on boxed integers 2: {0}", ((object)(long)29) == ((object)(int)29));
                Console.WriteLine("Equals() on unboxed integers 1: {0}", ((int)29).Equals((long)29));
                Console.WriteLine("Equals() on unboxed integers 2: {0}", ((long)29).Equals((int)29));
                Console.WriteLine("Equals() on boxed integers 3: {0}", object.Equals((long)29, (int)29));

    Gives this output:

    == on unboxed integers: True
    Equals() on unboxed integers 1: False
    Equals() on unboxed integers 2: True
    == on boxed integers 2: False
    Equals() on boxed integers 1: False
    Equals() on boxed integers 2: False
    Equals() on boxed integers 3: False

    So Equals() works in exactly one case, which is not commutative (while == does work thanks to integer promotions).

     



  • @Medezark said:

    The practical issue is that Java can't downconvert a Long to an Integer, even if the Long is in scope of Int.
     

    That is not a WTF. The WTF is that Java, one of the languages with the strictest type safety that I know of, doesn't give a shit that you pass a Long to to the getter of an Integer map. And no, Long does not extend Integer in Java.


  • FoxDev

    @Medinoc said:

    I just tested in C#, it's even worse than I thought:

                Console.WriteLine("== on unboxed integers: {0}", ((int)29) == ((long)29));
                Console.WriteLine("Equals() on unboxed integers 1: {0}", ((int)29).Equals((long)29));
                Console.WriteLine("Equals() on unboxed integers 2: {0}", ((long)29).Equals((int)29));
                Console.WriteLine("== on boxed integers 2: {0}", ((object)(long)29) == ((object)(int)29));
                Console.WriteLine("Equals() on unboxed integers 1: {0}", ((int)29).Equals((long)29));
                Console.WriteLine("Equals() on unboxed integers 2: {0}", ((long)29).Equals((int)29));
                Console.WriteLine("Equals() on boxed integers 3: {0}", object.Equals((long)29, (int)29));

    Gives this output:

    == on unboxed integers: True
    Equals() on unboxed integers 1: False
    Equals() on unboxed integers 2: True
    == on boxed integers 2: False
    Equals() on boxed integers 1: False
    Equals() on boxed integers 2: False
    Equals() on boxed integers 3: False

    So Equals() works in exactly one case, which is not commutative (while == does work thanks to integer promotions).

    You're mixing value comparisons with reference comparisons. Equals() will always do a reference comparison unless overridden. Equals() is also not guaranteed to be commutative. Not to mention Object.Equals() always does a reference comparison, regardless of the argument types (as they'll always be upcast to Object).

     



  • @Faxmachinen said:

    @Medezark said:

    The practical issue is that Java can't downconvert a Long to an Integer, even if the Long is in scope of Int.
     

    That is not a WTF. The WTF is that Java, one of the languages with the strictest type safety that I know of, doesn't give a shit that you pass a Long to to the getter of an Integer map. And no, Long does not extend Integer in Java.


    I was thinking in scope as in All Members of Set A are also Members of Set B;  All Integer's are members of the Long Integer Set (however the converse is not necessarily true); Conversion of an INT to a LONG should always be successful without loss of precision.  However, all LONG's are not able to be converted to INT's (They may go out of bounds);

    Natural numbers are a subset of Integers

    Integers are a subset of Rational Numbers

    Rational Numbers are a subset of the Real Numbers


  • Discourse touched me in a no-no place

    @mt@ilovefactory.com said:

    But having equality between Long and Integer would not be enough for the code to work. For this code to work work, it is also require that Long.hashCode() returns the same values as Integer.hashCode() would, when constructed with the same int. (That is: Long(n).hastCode()==Integer(n).hachCode). Which it does for the java implementation I currently use, but that is an implementation detail, not a documented feature you can relay on.
    Actually, you should be able to rely on two things that are intended to be equal according to equals() to also return the same hashCode() value; that is documented, and has been for a long time. (OTOH, equal hashes don't imply equality; the equality test is strictly more discriminating. By definition.)



  • @Medezark said:

    @joe.edwards said:

    @cconroy said:

    @pjt33 said:

    @eViLegion said:
    The WTF isn't java... ok so it includes some quirky stuff that has the possibility to be misused.
    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.
    In other words... TRWTF is Java. I get bitten by this all the time. I'm still hoping they change this in a future release but I'm not holding my breath.

    What was the argument against fixing it? Except, you know, a general policy of keeping all bugs around forever.

    I would assume that at this point it is a matter of backward compatability.  If applications were written to take advantage of the type casting differences they would break once the equivalency issue were fixed.

    Backwards compatibility is one reason, but the main reason seems to be that get(), contains(), etc. retrieve by equality which is not limited to objects of the same class. See e.g. http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic for some discussion of this. Personally I think the potential for bugs here outweighs the possible benefits, but apparently the language designers disagreed.

     



  • @RaceProUK said:

    Equals() will always do a reference comparison unless overridden.
    And it's overridden for the basic types, so I'm not sure what your point is.



  • @toface said:

    The problem is imho that the Map interface has the method V get(Object key) instead of V get(K key). If Java had generics from the start, this would've been one less pitfall, but it still only something a junior Java developer should do.

    C# never had any momentum before 2.0, so for MS it was a small cost to break backwards compatibility. For Sun (please don't mention the O-word) it was more important with backwards compatibility. 10 years later it's easy to say that it makes it easier to do a mistake, but I still think they made the right one.

    But backwards compatibility doesn't enter into it. The way they kept backwards compatibility was with erasure and raw types. Since the raw type of [code]Map[/code]'s [code]K[/code] is [code]Object[/code], they could have defined [code]get[/code] correctly in 1.5. (I actually set up a hacked build process which prepended a sensible version of [code]java.util[/code] to the bootclasspath, and it saved me some aggravation).


  • @cconroy said:

    Backwards compatibility is one reason, but the main reason seems to be that get(), contains(), etc. retrieve by equality which is not limited to objects of the same class. See e.g. http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic for some discussion of this. Personally I think the potential for bugs here outweighs the possible benefits, but apparently the language designers disagreed.
    (Directed at that SO question) Such BS.  That's a hack answer and would be solved by Java having a "SequenceEquals" method.



  • @Ben L. said:

    We have interfaces for that shit. If your program requires the use of primitives in a data structure but you don't know what primitives it'll use, YOU are TRWTF.

    Because nobody would use something like JSON!


  • Discourse touched me in a no-no place

    @Sutherlands said:

    That's a hack answer and would be solved by Java having a "SequenceEquals" method.
    What?? sequenceEquals() please…



  • @joe.edwards said:

    @cconroy said:
    @pjt33 said:
    There is a definite Sun WTF here. When they introduced generics, during the early-release phase a number of those of us who were experimenting with them argued that Map<K,V>.get should require a K for an argument. If they had listened it would have saved many people many hours of debugging. .Net got this right.
    In other words... TRWTF is Java. I get bitten by this all the time. I'm still hoping they change this in a future release but I'm not holding my breath.

    What was the argument against fixing it? Except, you know, a general policy of keeping all bugs around forever.

    The general policy is that Java should be backwards-compatible (old class files still work) and forwards compatible (old source can still be compiled). With the genericisation of [code]java.util[/code], Sun (specifically Neal Gafter, if snoofle is looking for names of language designers to hurt) decided to go even further and say that if people have written stupid code, they should be able to genericise it without being required to fix it. To quote from an old thread:

    @gafter said:

    Because different classes of objects can be equal() to each other, you can look up a key of one class by using a key of another class. Using get(K) would mean some legitimate code would not be able to be genericized because the compiler would reject the code.

    (The thread is Map.get(K) and Map.get(Object), but good luck making sense of it - in a massive Oracle WTF, when they migrated Sun's forum data they merged all users who didn't respond promptly to an e-mail to recover their accounts into user 843793, who thus appears to have a serious case of multiple personality disorder).

    PS cconroy, I deliberately said "a definite Sun WTF" because there are sufficiently many here that I don't want to single one out as TRWTF.


  • FoxDev

    @Sutherlands said:

    @RaceProUK said:
    Equals() will always do a reference comparison unless overridden.
    And it's overridden for the basic types, so I'm not sure what your point is.
     

    My point is, unless there's an override of Equals() that fits a certain scenario e.g. Int32.Equals(Int64), then you'll get the default, which is a reference comparison.

    Do I really have to describe virtual methods to you?


Log in to reply