In the way of learning Java Generics, I got stuck at a point.
It was written "Java Generics works only with Objects and not the primitive types".
e.g
Gen<Integer> gen=new Gen<Integer>(88); // Works Fine ..
But, with the primitive types like int,char etc ...
Gen<int> gen=new Gen<int>(88) ; // Why this results in compile time error
I mean to say, since java generics does have the auto-boxing & unboxing feature, then why this feature cannot be applied when we declare a specific type for our class ?
I mean, why Gen<int> doesn't
automatically get converted to
Gen<Integer> ?
Please help me clearing this doubt.
Thanks.
Autoboxing doesn't say that you can use int instead of Integer. Autoboxing automates the process of boxing and unboxing. E.g. If I need to store some primitive int to a collection, I don't need to create the wrpper object manually. Its been taken care by Java compiler. In the above example you are instantiating an generic object which is of Integer type. This generic object will still work fine with int but declaring int as a generic type is wrong. Generics allow only object references not the primitives.
As you have discovered, you can't mention a primitive type as a type parameter in Java generics. Why is this the case? It is discussed at length in many places, including Java bug 4487555.
The simple explanation: Generics are defined that way.
A good reason from the Java perspective: It simplifies type erasure and translation to byte code for the compiler. All the compiler needs to do is some casting.
With non-primitives the compiler would have to decide whether to cast or to inbox/outbox, it would to need to have additional validating rules (extends and & wouldn't make sense with primitives, should a ? include primitives, yes or no? and so on) and have to handle type conversions (assume you parametize a collection with long and add an int...?)
A good reason from a programmers perspective: operations with a bad performance are kept visible! Allowing primitves as Type Arguments would require hidden autoboxing (inboxing for store, outboxing for read operations. Inboxing may create new objects which is expensive. People would expect fast operations if they parametize a generic class with primitives but the opposite would be true.
That's a very good question.
As you suspected, the abstraction could surely be extended to the type parameters, and made them trasparent to the programmer. In fact, that is what most modern JVM languages do (statically typed ones, of course). Examples include Scala, Ceylon, Kotlin etc.
This is what your example would look like in Scala:
val gen: Gen[Int] = new Gen[Int](80)
Int is just a regular class, just like other classes. There is no primitive-object distinction whatsoever.
As to why Java people did not do it... I don't actually know the reason, but I imagine such an abstraction would not fit with the existing Java specification without overcomplicating the semantics (or without sacrificing the backward compatibility, which is certainly not a viable option).
Related
I know that Java does not allow primitive data types to be used in Generics, i.e
List<int> l = new List<int>();
is not allowed.
I have read a related post which states that this is for the purpose of backward compatibility. Can anyone explain how not allowing primitives to be used in generics maintains backward compatibility? I would greatly appreciate a small explanation with an example.
One sub question: what is/are major/minor drawbacks of how generics are implemented in java.
Your response will be greatly appreciated.
As explained here (with examples), generics in Java only exist at compile time. Under the hood, a generic collection is really a non-generic collection, and all their contents are stored as Object. In pre-generics times (pre Java 5), collections could not contain primitives because they cannot be down-casted to Object, and they still cannot.
To answer your question: Allowing generic collections to store primitives would not be a breaking change because generic collections did not exist previously, and thus the primitive decision has nothing to do with backward compatibility. If they decided to implement generics like in C#, primitive generic collection types would exist, and it would not change the behavior of previous programs.
The reasons as to why they implemented generics this way, though, might be related to the fact that this is a low-energy approach: They barely had to add any code to collections to support this feature, and they did not have to change the JVM. This improves maintainability. That does not sound like much of a reason, but it might actually be very important if you want every future JVM be able to still execute Java 1.0 code until the end of time.
Java does not allow primitive types to be used in generic data structures. E.g. ArrayList<int> is not allowed. The reason is, primitive types can not be directly converted to Object. However Java 1.5 does support auto-boxing, and wrapper classes work in generic data structures. So why couldn't the compiler auto-box it to ArrayList<Integer>? Are there any other reasons for why this can not work?
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?)
The generic type information is erased at run time. Check this link. Generics have more to do with compile time checking than run time checking. The autoboxing and unboxing are the run time operations. See the link. This is the reason that autoboxing should not work with Generics.
The problem will be in performance. For every get()/set() method, in the list, the JVM will have to unbox/box the respective value for the mentioned method respectively. Remember, autoboxing take primitive types and wraps them into an Object and vice-versa, as stated on Autoboxing:
Finally, there are performance costs associated with boxing and
unboxing, even if it is done automatically.
I think they wanted a List to do simple operation and alleviating performance all together.
I don't think there's any technical reason it couldn't be done like you say, but there are always interface considerations: e.g., if you automatically converted objects of type ArrayList<int> to be ArrayList<Integer>, you lose some explicitness in terms of the interface specifications: it is less obvious that ArrayList in fact store objects, not primitives.
My understanding is that autoboxing is more for compatibility and flexibility in parameter types than for the ease of being able to say "int" instead of "Integer." Java's not exactly known for it's obsession with conciseness...
A small P.S.: I don't think it would technically be correct to say "autobox ArrayLint<int> to ArrayList<Integer>," because you aren't actually wrapping anything in an object "box" -- you're just actually converting a typename ArrayList<int> to "actual" type ArrayList<Integer>
I'm glad it is impossible, because int use much less memory than Integer and is much faster too. Therefore it forces me to think whether it is acceptable to use Collection<Integer> or not (lot of times in business application it's ok, but in other apps it is not).
I would be much happier if Collection<int> was possible and efficient, but it is not.
I don't think this is any sort of problem - do you have any concrete case where is this limiting you somehow? And btw there is difference between int and Integer while the object can be null and primitive type can't.
I am confused about when to use primitive vs. non-primitive(?) types (i.e. int vs. Integer) in Java. I realize that in some places you can't use primitive types (for example when making use of generics). But what about in "normal" code? Is there a performance penalty for using non-primitive types? What about when working with Android?
***My question is very similar to this question, which was discovered by one of the posters below. The answers to the linked question give additional insights into this question, which are not covered below.
*** "non-primitive" types are officially referred to as reference types.
Short answer: An int is a number; an Integer is a pointer that can reference an object that contains a number. Using Integer for arithmetic involves more CPU cycles and consumes more memory. An int is not an object and cannot passed to any method that requires objects (just like what you said about Generics).
Non-primitive types are objects. They have to be dynamically allocated, garbage collected, and checked for null-ness (although some of these operations may get removed by an optimizing compiler). Reading their actual value requires loading from a pointer. Primitive types are values. They generally take up less space and are faster to access.
A good rule of thumb is, use primitive types unless you need polymorphism, in which case use the corresponding object.
There is a slight penalty for converting between the types (autoboxing). Also int will have a bit less overhead so I would always go with int if you can.
Also see this question: When to use primitive and when reference types in Java
In Java, int is a primitive data type, while Integer is a Wrapper class.
int, being a primitive data type has less flexibility. We can only store the binary value of an integer in it.
Since Integer is a wrapper class for int data type, it gives us more flexibility in storing, converting and manipulating integer data.
Integer is a class and thus it can call various in-built methods defined in the class. Variables of type Integer store references to Integer objects, just as with any other reference (object) type.
You can find a more detailed explanation here.
As an OO purist, you would likely shun the primitives altogether and damn the performance costs and lack of postfix operators. (Yes, there is a performance cost.) You may also adopt this approach simply from extensibility considerations as a designer (without necessarily being hung up on purity.)
As a practical matter (outside of theoretical and aesthetic questions), use the primitives everywhere you can and use the object version where you can't use primitives. (You already mentioned one such case. The language and APIs will drive this decision.)
As a performance freak, you would likely shun the object versions and you may not really care too deeply if you step on a few OO golden rules and sacrosanct no-goes: performance is king and you make your decisions accordingly.
I'd recommend option 2 as a good place to start until you develop your own dogmatic preferences! :)
My view: Using Integer as parameters or return values allows one thing that primitive ints don't allow: Using null. But is this a good idea? I think it rarely ever is.
As far as performance is concerned: The compiler will optimize your code to some degree, so that is most of the time not a real concern.
There are plenty of questions on stackoverflow from people who have attempted to create an array of generics like so:
ArrayList<Foo>[] poo = new ArrayList<Foo>[5];
And the answer of course is that the Java specification doesn't allow you to declare an array of generics.
My question however is why ? What is the technical reason underlying this restriction in the java language or java vm? It's a technical curiosity I've always wondered about.
Arrays are reified - they retain type information at runtime.
Generics are a compile-time construct - the type information is lost at runtime. This was a deliberate decision to allow backward compatibility with pre-generics Java bytecode. The consequence is that you cannot create an array of generic type, because by the time the VM wants to create the array, it won't know what type to use.
See Effective Java, Item 25.
Here is an old blog post I wrote where I explain the problem: Java generics quirks
See How do I generically create objects and arrays? from Angelika Langer's Java Generics FAQ for a workaround (you can do it using reflection). That FAQ contains everything you ever want to know about Java generics.
You are forced to use
ArrayList<Foo>[] poo = new ArrayList[5];
which will give you an unchecked warning. The reason is that there is a potential type safety issue with generics and the runtime type-checking behavior of Java arrays, and they want to make sure you are aware of this when you program. When you write new ArrayList[...], you are creating something which will check, at runtime, everything that gets put into it to make sure that it is an instance of ArrayList. Following this scheme, when you do new ArrayList<Foo>[...], then you expect to create something that checks at runtime everything that gets put into it to make sure it is an instance of ArrayList<Foo>. But this is impossible to do at runtime, because there is no generics info at runtime.
Java collections only store Objects, not primitive types; however we can store the wrapper classes.
Why this constraint?
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.
Makes the implementation easier. Since Java primitives are not considered Objects, you would need to create a separate collection class for each of these primitives (no template code to share).
You can do that, of course, just see GNU Trove, Apache Commons Primitives or HPPC.
Unless you have really large collections, the overhead for the wrappers does not matter enough for people to care (and when you do have really large primitive collections, you might want to spend the effort to look at using/building a specialized data structure for them).
It's a combination of two facts:
Java primitive types are not reference types (e.g. an int is not an Object)
Java does generics using type-erasure of reference types (e.g. a List<?> is really a List<Object> at run-time)
Since both of these are true, generic Java collections can not store primitive types directly. For convenience, autoboxing is introduced to allow primitive types to be automatically boxed as reference types. Make no mistake about it, though, the collections are still storing object references regardless.
Could this have been avoided? Perhaps.
If an int is an Object, then there's no need for box types at all.
If generics aren't done using type-erasure, then primitives could've been used for type parameters.
There is the concept of auto-boxing and auto-unboxing. If you attempt to store an int in a List<Integer> the Java compiler will automatically convert it to an Integer.
Its not really a constraint is it?
Consider if you wanted to create a collection that stored primitive values. How would you write a collection that can store either int, or float or char? Most likely you will end up with multiple collections, so you will need an intlist and a charlist etc.
Taking advantage of the object oriented nature of Java when you write a collection class it can store any object so you need only one collection class. This idea, polymorphism, is very powerful and greatly simplifies the design of libraries.
The main reason is the java design strategy.
++
1) collections requires objects for manipulation and primitives are not derived from object
so this can be the other reason.
2) Java primitive data types are not reference type for ex. int is not an object.
To Overcome:-
we have concept of auto-boxing and auto-unboxing. so if you are trying to store primitive data types compiler will automatically convert that into object of that primitive data class.
I think we might see progress in this space in the JDK possibly in Java 10 based on this JEP - http://openjdk.java.net/jeps/218.
If you want to avoid boxing primitives in collections today, there are several third party alternatives. In addition to the previously mentioned third party options there is also Eclipse Collections, FastUtil and Koloboke.
A comparison of primitive maps was also published a while ago with the title: Large HashMap overview: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove. The GS Collections (Goldman Sachs) library was migrated to the Eclipse Foundation and is now Eclipse Collections.