cyberion wrote:Java 5 and onwards allows type variables and parametrized types to be used in declarations. 'Type Erasure' is a grandiose term describing a "process" whereby the compiler eliminates all references to these type variables and parameters in the generated byte code. The reason given is to make Java 5 and onwards compatible with earlier versions of Java, which do not support Generics.
What I suspect is the case is that Generics has been added as incomplete, "design time only" support for parameterized types, which the compiler IGNORES at the point of actual generation of byte code -- hardly what I would call a "process of type erasure". I suspect that the compiler simply does nothing different, and generates the same byte code as the equivalent raw types. The only compiler difference is in its design time validation.
Backward compatibility is a good reason for allowing aliasing of raw types with generic types. It is not a good reason for allowing heap pollution. Heap pollution causes pending runtime exceptions which should be trapped immediately. This suggests that parametrized type information is important at runtime, even when you require backward compatibility.
Can anyone prove me wrong, perhaps by creating a concise example of a generic type that compiles into different byte code from its equivalent raw type?
I have not read the full thread; as far as I can tell, you were arguing about whether the JVM design is good or not; I don't claim to be an expert field, but I have some comments about statements in your original post which are wrong.
This dependes on your definition of "generation of byte code". The bytecode, as my definition goes, is the whole class file. In a class file compiled with generics, additional information is present in comparison to the equivalent raw code, obviously. The best evidence is when you compile code against java.util.List, which is a generic class you only have in the form of bytecode. If the information was lost, you could not base your own generic code on precompiled generic code. Thus, the Java compiler does not simply discard generic information, neither for validating your classes, nor for generating the bytecode.
If you only consider the opcodes which are part of methods bytecode, then, yes, you could create a raw class which generates the same "bytecode". An elegant way to put it is that *objects* don't have a generic type, but fields, types and methods do. Note that the compiler is not the only tool which can access this information. The information is not only preserved in the bytecode, but also at runtime.
Type is the superinterface for all types, including generic types and you can e.g. query a Field for its generic type.
There are patterns which come close to objects having generic types at runtime, e.g. used by the Collections.checked*() methods. However, this does only work if you have an actual class which you can add code to; it does not work for arrays. Arrays are where generics break down, but if you write good code, this does not matter. Bad design is not when things can break (otherwise you'd have to argue that C++ is designed badly because you can declare destructors nonvirtual, or that C is designed badly because it allows arbitrary pointer manipulation); bad design is when things break when you do normal stuff. Casting a List<String>[] to Object[] to inject a rogue object is not normal - passing an array you still need to a method you don't trust is not normal in the first place. You can't control what is put into an array, independently of whether you use generics. If you don't trust the code, give them an object that mediates between your data and them. If you don't need the data afterwards, simply catch the exception. Generics or not, a rogue method will be able to throw a ClassCastException.