You're asking about primitive types and objects, but I don't think that's a useful distinction here. Instead, you should be thinking about reference types and value types:
- Value types: always exactly of the specified type (can't be a derived type instead); can't use virtual function dispatch; lifetime tied to the scope of the variable (usually allocated on the stack)
- Reference types: can be the specified type or a derived type; can use virtual function dispatch; lifetime not tied to the scope of the variable (usually allocated on the heap)
If you look at these differences, you realize that both kind of types have some merit: value types are more performant, while reference types are more flexible, especially if you want to use OOP features like inheritance or virtual functions.
This is why many languages (including C++, C# and Java) offer both of them in one form or another (though the form varies widely).
Now we have two kinds of types, but we would also like to have a unified type system. And that means having a type, where variables of this type can contain values of any type. This requirement means that the type (called Object
in C# and Java) has to be a reference type. And to convert a value type to this Object
type, you have to “box” it: create a copy of the value that acts like a reference type.
To sum up: a language has to support boxing, if you want to have reference types, value types and a unified type system.