Auto-Boxing, Equals and all that.
Monkey Forums/Monkey Programming/Auto-Boxing, Equals and all that.
| ||
The docs are a bit quiet on the subject of Monkey's auto-boxing and as I tripped over it while porting box2d I'm wondering if we could get some clarification about intentions. It looks like Monkey is following the Java model, is that the case? If so, then are you also going to uncomment the Equals method on the Object definition so we have a standard way to test equality by value? Also, there seems to be an inconsistency with the Java model when handling overloaded method resolution. Java will use a method that doesn't require boxing/unboxing in preference to one that does. Monkey currently throws an error if you have an overload that takes an Object reference as well as one that takes the unboxed type and call with the boxed type. I'll put some test code here that shows the problem as well as demonstrating the other behaviours for anyone interested: Class AutoBoxTest Method Test(val:IntObject) Print "IntObject method called" End Method Test(val:Int) Print "Int method called" End End Class AutoBoxTest2 Method Test(val:Object) Print "Object method called" End Method Test(val:Int) Print "Int method called" End End Class BoxyCollection<T> Private Field arr : T[] = New T[10] Public Method Get:T( index:Int) If( index >=0 And arr.Length > index ) Return arr[index] Else Return Null End End Method Set( index:Int, item:T ) If( index >= arr.Length ) arr = arr.Resize(index+10) End arr[index] = item End End Function Main() Local io1:IntObject = 1 Local io2:IntObject = 1 Local ip1:Int = io1 Local ip2:Int = io2 Local tester:AutoBoxTest = New AutoBoxTest() tester.Test(io1) tester.Test(ip1) If io1 = io2 Print "IntObjects are considered equal by value" Else Print "IntObjects aren't considered equal by value" End If io1 = ip1 Print "IntObject and Int primitive are considered equal" Else Print "IntObject and Int primitive are considered unequal" End Local bc:BoxyCollection<IntObject> = New BoxyCollection<IntObject>() bc.Set(0,ip1) If bc.Get(0) = ip1 Print "Int set and retrieved and compared successfully" Else Print "Int set and retrieved and comparison failed" End Local tester2:AutoBoxTest2 = New AutoBoxTest2() tester2.Test(io1) 'Unable to determine overload tester2.Test(ip1) End |
| ||
Hi, The boxing stuff is based on my experience with Java about 10 years ago! But it's not really based on Java's 'model' or anything, I just did it the way I thought would be most useful. In Java, I seem to remember you couldn't do this... IntObject x=10; ...but maybe you can now. > Also, there seems to be an inconsistency with the Java model when handling overloaded method resolution. I prefer it Monkey's way, as there's absolutely no confusion and keeps the overloading rules simple. And in the real world, wouldn't/shouldn't the method param be IntObject instead of Object anyway? But perhaps a modification could be made to the overloading rules, eg: a single 'upcast' param match could be considered an exact match? This is something I've considered for other reasons too, and it should work here too. |
| ||
To be honest, I find the idea of having to cast a descendant of Object to Object in order to persuade the language that I actually believe it is what it is to be confusing. If I want to pass the value to the primitive overload then the ToPrimitive() methods there to do just that. Anyway, opinions aside, the request for some sort of clarifying documentation stands. Edit: And the question about the Equals method. |
| ||
Since Java 5, the compiler has autoboxed/unboxed for you. So yes you can do both: Integer x = 10; int y = x; Just a little tip when manually boxing integers: Never use new Integer(int) because it ALWAYS creates a new object. Java keeps a pool of Integer objects for you to reuse, so always use Integer.valueOf(int). It'll read from the pool first, or create one if necessary. |
| ||
I'm afraid I'm going to pick up on this again, because it's niggling me. The attraction of auto-boxing and unboxing is meant to be that you can use wrappers for primitives without the annoying overhead of typing the conversions all over the place. So, in theory, it's great for something like a general data wrapper because you can add the "ToPrimitive" methods and a bunch of overloaded constructors/factory methods to take the primitives and life becomes very easy. For example, with a JSONObject defined in this way, I can do the following: Local root:JSONObject = New JSONObject() root.AddItem("name",name) 'name is a String root.AddItem("width",width) 'width and height are Floats root.AddItem("height",height) root.AddItem("bounded",bounded) 'bounded is a Bool name = root.GetItem("name") width = root.GetItem("width") height = root.GetItem("height") bounded = root.GetItem("bounded") Good stuff. Super easy to use. However, like most general-purpose data-structures, JSON is recursive. A JSON object or array can contain JSON objects and arrays. So, I need to be able to add those too. Local playerJSON:JSONObject = New JSONObject() playerJSON.AddItem("posX",player.position.x) playerJSON.AddItem("posY",player.position.y) root.AddItem("player",playerJSON) Well, that would be nice, except I can't, because Monkey gets its pants in a knot over the fact that there are methods that take primitives as well as one that matches the unboxable object type. Instead, I have to have separate method names for primitives. This is not pretty, less easy to use, seems to go against the very reason for having auto-boxing in the first place and generally makes me an unhappy panda. |