views:

934

answers:

5

Hi,

This is not a question of what is boxing and unboxing, it is rather why do languages like Java and C# need that ?

I am greatly familiar wtih C++, STL and Boost.

In C++ I could write something like this very easily,

std::vector<double> dummy;

I have some experience with Java, but I was really surprised because I had to write something like this,

ArrayList<Double> dummy = new ArrayList<Double>();

My question, why should it be an Object, what is so hard technically to include primitive types when talking about Generics ?

+10  A: 

Boxing and unboxing are a necessity born out of the way that languages (like C# and Java) implement their memory allocation strategies.

Certain types are allocated on the stack and other on the heap. In order to treat a stack-allocated type as a heap-allocated type, boxing is required to move the stack-allocated type onto the heap. Unboxing is the reverse processes.

In C# stack-allocated types are called value types (e.g. System.Int32 and System.DateTime) and heap-allocated types are called reference types (e.g. System.Stream and System.String).

In some cases it is advantageous to be able to treat a value type like a reference type (reflection is one example) but in most cases, boxing and unboxing are best avoided.

Andrew Hare
Careful with value types and stack-based allocation. Eric Lippert had two great blog posts on that topic: http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx and http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx
Joey
AFAIK, C# does not use auto-boxing on generic containers unless you specify something like List<object>. C#'s generics work in a manner similar to C++ for value types. http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx
James Schek
+2  A: 

I believe this is also because primitives do not inherit from Object. Suppose you have a method that wants to be able to accept anything at all as the parameter, eg.

class Printer {
    public void print(Object o) {
        ...
    }
}

You may need to pass a simple primitive value to that method, like:

printer.print(5);

You would be able to do that without boxing/unboxing, because 5 is a primitive and is not an Object. You could overload the print method for each primitive type to enable such functionality, but it's a pain.

artemb
+1  A: 

In Java and C# (unlike C++) everything extends Object, so collection classes like ArrayList can hold Object or any of its descendants (basically anything).

For performance reasons, however, primitives in java, or value types in C#, were given a special status. They are not object. You cannot do something like (in Java):

 7.toString()

Even though toString is a method on Object. In order to bridge this nod to performance, equivalent objects were created. AutoBoxing removes the boilerplate code of having to put a primitive in its wrapper class and take it out again, making the code more readable.

The difference between value types and objects in C# is more grey. See here about how they are different.

Yishai
In C#, primitives are implemented as structs (int for example is defined in Int32 in the System namespace) that have methods so 7.ToString() will work just fine. And while everything in C# derives from Object, this is not the case for Java.
JulianR
@JulianR: More specifically, all CLR value types inherit from System.ValueType which in turn inherits from System.Object. System.ValueType overrides many of System.Object's virtual methods thus avoiding boxing when those methods are called. The only methods that cause boxing for a value type are Object.GetType and Object.MemberwiseClone.
Andrew Hare
+10  A: 

what is so hard technically to include primitive types when talking about Generics ?

In Java's case, it's because of the way generics work. In Java, generics are a compile-time trick, that prevents you from putting an Image object into an ArrayList<String>. However, Java's generics are implemented with type erasure: the generic type information is lost during run-time. This was for compatibility reasons, because generics were added fairly late in Java's life. This means that, run-time, an ArrayList<String> is effectively an ArrayList<Object> (or better: just ArrayList that expects and returns Object in all of its methods) that automatically casts to String when you retrieve a value.

But since int doesn't derive from Object, you can't put it in an ArrayList that expects (at runtime) Object and you can't cast an Object to int either. This means that the primitive int must be wrapped into a type that does inherit from Object, like Integer.

C# for example, works differently. Generics in C# are also enforced at runtime and no boxing is required with a List<int>. Boxing in C# only happens when you try to store a value type like int in a reference type variable like object. Since int in C# inherits from Object in C#, writing object obj = 2 is perfectly valid, however the int will be boxed, which is done automatically by the compiler (no Integer reference type is exposed to the user or anything).

JulianR
+1  A: 

I can only tell you for Java why it doesn't support primitve types in generics.

First there was the problem that the question to support this everytime brought on the discussion if java should even have primitive types. Which of course hindered the discussion of the actual question.

Second the main reason not to include it was that they wanted binary backward compatibility so it would run unmodified on a VM not aware of generics. This backward compatibility/migration compatibility reason is also why now the Collections API supports generics and stayed the same and there isn't (as in C# when they introduced generics) a complete new set of a generic aware Collection API.

The compatibility was done using ersure (generic type parameter info removed at compile time) which is also the reason you get so many unchecked cast warnings in java.

You could still add reified generics but it's not that easy. Just adding the type info add runtime instead of removing it won't work as it breaks source & binary compatibility (you can't continue to use raw types and you can't call existing compiled code because they don't have the corresponding methods).

The other approach is the one C# chose: see above

And automated autoboxing/unboxing wasn't supported for this use case because autoboxing costs too much.

Java theory and practice: Generics gotchas

jitter