3 ms·
Java generics works only on reference types (i.e. anything that derives from Object), and not on primitive types like int and long. For this reason, if you want
by mrighele 4y ago
Java generics works only on reference types (i.e. anything that derives from Object), and not on primitive types like int and long. For this reason, if you want a method that work on both primitives and reference types, you have to provide distinct implementations. This is true even if you provide a generic version like
public static <A> void sort(A[] e);
You will not be able to use the above method with an int[]. Whenever possible the compiler will try to be helpful, and automatically box/unbox primitives for you, so for example the following will work
public <A> A max(A a1, A a2);
/* ... */
void foo() {
int x = max(1,2);
}
this comes at the cost of boxing ints inside Integer, though.
So, if you ignore for a moment primitives types, whenever you have generics, everything boils down to a single method accepting Objects and returning Objects. What the JVM does is to do runtime profiling of what actually you are passing to the generic method, and generate optimized routines for the "best case". In theory this is the best of the two worlds, because (unlike, let's say, C++) you will have a single implementation of the method, avoiding code explosion, but if you use it in an hot spot you get the optimized code.
In a way, it is quite wasteful, because you throw away a lot of information at compile time, just to get it back (and maybe not all of it) at runtime through profiling, but in practice it works quite well.
A side effect of this is this makes the JVM a wonderful VM for running dynamic languages like Ruby and Python, because that information is _not_ there at compile time. In particular GraalVM/Truffle and exposes this functionality to dynamic language implementations, allowing very good performance (according to their website [1][2], Ruby and Python on Truffle are about 8x faster than the official implementation, and JS in line with V8)
[1] https://www.graalvm.org/ruby/ https://www.graalvm.org/ruby/
[2] https://www.graalvm.org/python/ https://www.graalvm.org/python/
- amenghra 4y agoThe state of Java’s type erasure makes me quite sad when I need the type information via introspection. I’d say the best situation would be keep the type information at compile time but let the VM decide if multiple implementations are needed or not at runtime.
- admax88qqq 4y agoWhat information do you need that's gone? Type erasure happens but the objects still carry their true dynamic type with them, isn't that sufficient?
- cstrahan 4y agoThe arguments still carry their dynamic type, but you lose the type parameters for the functions applied to those arguments. Of course, at the call sites for known working code you can infer the type parameters -- let's say for example that the function has one type parameter and one argument of that same type: use reflection to get the of that argument and, under the premise that you're code doesn't have a runtime type error, you know the type param for that function is the very same. However, what if you have a collection of generic functions divorced from the arguments? There's no way from the function alone to determine the type params. So you can't write code that reflects over a collection of generic functions and dispatches solely on the type params. And, while I've been speaking of functions, this really generalizes to any generic type. If you have e.g. a collection of PetTrainer<?>, and you have a code path in which you have a pet of type Dog or Cat, how can you use only reflection to choose a trainer upon which you can call Train(pet)? You can't. (I don't Java, so please give me a little slack wrt syntax.)
- admax88qqq 4y agoSounds like a missed opportunity for polymorphism. The whole point of OO and polymorphism is to do let the dynamic dispatch call the right method body for you. Implementing that by hand using reflection just sounds plain wrong. Would love to hear of a concrete real world example where this was an issue
- anonymoushn 4y agoJava users have to use double dispatch and such, because Java only implements dynamic dispatch on the type-erased type of the first function argument. The last bit of the GP comment is an example of the sort of thing that isn't possible with generics in Java, because the PetTrainer-of-Dogs and PetTrainer-of-Cats are indistinguishable at runtime. If you want to solve the problem with dynamic dispatch on the type-erased type of the first argument only, you could *stop using generics*, write a separate class for each generic instantiation you would have had, send every pet to every trainer, and have each trainer check the runtime type of their argument and do nothing if it's the wrong type. This probably isn't what anyone really wants to write or an acceptably cheap thing to do at runtime. Nobody actually deals with PetTrainers but people do deal with MessageSubscribers that work more or less exactly as described, and the type system does nothing to help. For a concrete example of the horrors of double dispatch, you could see the wonderful book Crafting Interpreters.