Why Don't Java Generics Support Primitive Types

Why don't Java Generics support primitive types?

Generics in Java are an entirely compile-time construct - the compiler turns all generic uses into casts to the right type. This is to maintain backwards compatibility with previous JVM runtimes.

This:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

gets turned into (roughly):

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

So, anything that is used as generics has to be convertable to Object (in this example get(0) returns an Object), and the primitive types aren't. So they can't be used in generics.

Why not auto-box Java primitive types for Generics?

So as far as I understand it, your proposed ArrayList<int> would be identical to ArrayList<Integer>. Is that right? (In other words, internally it still stores an Integer; and every time you put something in or get it out, it would automatically box/unbox it, but autoboxing/autounboxing already does that for ArrayList<Integer>.)

If it is the same, then I don't understand what the utility of having a duplicate syntax <int> is when it means the same thing as <Integer>. (In fact it will introduce additional problems, because for example int[] is not the same runtime type as Integer[], so if you have T[], and T is int, what would it mean?)

Why can Java Collections not directly store Primitives types?

It was a Java design decision, and one that some consider a mistake. Containers want Objects and primitives don't derive from Object.

This is one place that .NET designers learned from the JVM and implemented value types and generics such that boxing is eliminated in many cases. In CLR, generic containers can store value types as part of the underlying container structure.

Java opted to add generic support 100% in the compiler without support from the JVM. The JVM being what it is, doesn't support a "non-object" object. Java generics allow you to pretend there is no wrapper, but you still pay the performance price of boxing. This is IMPORTANT for certain classes of programs.

Boxing is a technical compromise, and I feel it is implementation detail leaking into the language. Autoboxing is nice syntactic sugar, but is still a performance penalty. If anything, I'd like the compiler to warn me when it autoboxes. (For all I know, it may now, I wrote this answer in 2010).

A good explanation on SO about boxing: Why do some languages need Boxing and Unboxing?

And criticism of Java generics: Why do some claim that Java's implementation of generics is bad?

In Java's defense, it is easy to look backwards and criticize. The JVM has withstood the test of time, and is a good design in many respects.

Java permits primitive types in generics

Both Byte.class and Byte.TYPE are Class<Byte> objects. The latter are just used to distinguish between primitive type and object type.

Actually Byte.TYPE is defined as:

public static final Class<Byte> TYPE = (Class<Byte>) Class.getPrimitiveClass("byte");

and getPrimitiveClass is an opaque method which retrieves the type from the VM so we can't investigate it further.

So, even if you think that you are passing a primitive data type Class, since they don't exist (why should they, since they refer to something that is typable according to the Java typing system for objects, which doesn't include primitive types until they are boxed into wrapper classes), you are creating a Vector<Byte>.

But in the end this doesn't matter much, upon reaching run-time execution type annotations are erased and the generic type doesn't mean anything. Whenever you'll add a byte it will be autoboxed to a Byte object and that's it.

I have no way to test your code at the moment, which exceptions are thrown at runtime when adding items to the Vector?

why Generic types only for Object?

It's not just wildcards - Java generics simply don't support primitive types as type arguments at all.

See the Java Generics FAQ for more information about Java Generics in general, and this question in particular.

Why is it not possible use primitive types with polymorphic return types?

As always with these questions, the answer is that you'd have to ask the language designers. I can't see any reason why this couldn't be done. However in my opinion this feature would be fairly pointless. As you point out in the question it's only when moo is invoked on a variable of static type IntFoo that a primitive would get returned; on a variable of type Foo<Integer>, an Integer would get returned anyway. So you can achieve essentially the same thing by doing this.

public class IntFoo implements Foo<Integer> {

@Override
public Integer moo() { return mooAsInt(); }

public int mooAsInt() { return 0; }
}

Personally I think this is better because it's much more obvious when boxing does / does not take place. In your proposal, moo() could return an int or an Integer depending on the static type of the variable, which would be extremely confusing.

Generics in conjunction with primitive objects

My first idea is to wrap each int in an object<T>, and just feed it in.

You don't have to - that is what java.lang.Integer is for. Similar wrapper types exist for all other primitive types of Java.

Is this a clean solution, or is there more of a best practices approach?

It depends on your performance requirements. If wrapping primitives is acceptable (it is for the majority of applications) then you have a working approach.

In the unlikely scenario that this approach is not fast enough you would need to build special classes for each primitive type, in a way similar to Java's take on streams for primitives (IntStream, LongStream, DoubleStream, etc.)



Related Topics



Leave a reply



Submit