how to declare Class.class with valid generics - java

Note: purely out of curiosity and not for any actual use case.
I'm wondering if there is a way to declare the Class Class object with valid type parameters:
Class cc1 = Class.class; //raw type
Class<Class> cc2 = Class.class; //now parameter is raw type
Class<Class<?>> cc3 = Class.class; //compile error: inconvertible types
If Class and Class<?> are interchangeable, why are Class<Class> and Class<Class<?>> not?
EDIT: the question can be generalized to an issue of nested raw type parameters. For example:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList>(); //same compile error
EDIT2: I should rephrase the question a little: I know that
Class<?> c = Class.class;
is valid but I'm wondering why Class<Class> is not the same as Class<Class<?>>

Generics have some pretty serious limitations. In this case you can't assign a type to the inner type of Class<Class> because you're actually referencing the raw type, not an implementation of the raw type. It will give you a warning, but you have no way to fix that warning.
Class<Class<?>> by itself isn't an inconvertible type, you just can't assign a class directly to it because it doesn't have the type Class<Class<T>>, it has the type Class<T>.
Think of it another way; try List<List<String>>. To create that, you need to create a List that takes a List of Strings. This works because lists can contain lists.
A Class is more like a primitive than a data object, so I don't think it'd be possible to create a Class that is of type Class of something else.
Edit: your extra question about ArrayList<ArrayList<?>> is a more obvious example of the inconvertible type issue you're having with Class<Class<?>>.

The rule here is that the generic type in the left side must match the generic type in the right side.
Class<?> means a class of any type.
Class<?> c = Class.class;
Works because Class of any type can be Class<Class>.
Class<Class<?>> cc3 = Class.class;
Do not work because Class.class type is Class<Class> which is not of type Class<Class<?>>
ArrayList<ArrayList<Integer>> lst = new ArrayList<ArrayList<Integer>>();
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<?>>();
Works because the two expressions match.
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList>();
Don't match.

It's kind of difficult to see exactly what you're asking (or what you're trying to do).. but you can parametrize without raw types.
Class<? extends Object> cc4 = Class.class; // no raw types
Class<?> cc5 = Class.class; // also an option
As far as your last example is concerned, it makes no sense because it appears you want to make an array list of array lists that hold ?, but your declaration isn't declaring an array list of array lists that hold ?.
Properly written (but still not proper Java) would be:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<Integer>>(); // Type mismatch
Which is expected. It doesn't work for the same reason something like the following doesn't work:
Object o = new Object();
Integer i = new Integer(3);
o = i;
i.floatValue();
o.floatValue(); // can't access that method, we need to specifically cast it to Integer
Java types aren't proactively inferred (even in an inheritance chain).
If you want to keep the wildcard in there, you're welcome to, though:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<?>>(); // works!

Related

Why is usage of instanceof is not allowed, but Class.isInstance() is allowed in Generics?

I was reading about Generics from ThinkingInJava and found this code snippet
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg) {
if(arg instanceof T) {} // Error
T var = new T(); // Error
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[SIZE]; // Unchecked warning
}
}
I understand the concept of erasure and I know that at runtime, there is no type for T and it is actually considered an Object (or whatever the upper bound was)
However, why is it that this piece of code works
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
}
Shouldn't we apply the same argument here as well that because of erasure we don't know the type of T at runtime so we can't write anything like this? Or am I missing something here?
In your example, T is indeed erased. But as you pass kind, which is the class object of the given type, it can be perfectly used for the said check.
Look what happens when you use this class:
ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);
Here, the class object of String is passed to the given constructor, which creates a new object out of it.
During class erasure, the information that T is String is lost, but you still have
ClassTypeCapture capture = new ClassTypeCapture(String.class);
so this String.class is retained and known to the object.
The difference is that you do have a reference in the second snippet to an instance of java.lang.Class; you don't have that in the first.
Let's look at that first snippet: There is only one instance of Erased as a class. Unlike, say, C templates which look a bit like generics, where a fresh new class is generated for each new type you put in the generics, in java there's just the one Erased class. Therefore, all we know about T is what you see: It is a type variable. Its name is T. Its lower bound is 'java.lang.Object'. That's where it ends. There is no hidden field of type Class<T> hiding in there.
Now let's look at the second:
Sure, the same rule seems to apply at first, but within the context of where you run the kind.isInstance invocation, there's a variable on the stack: kind. This can be anything - with some fancy casting and ignoring of warnings you can make a new ClassTypeCapture<String>() instance, passing in Integer.class. This will compile and even run, and then likely result in all sorts of exceptions.
The compiler, just by doing some compile time lint-style checks, will really try to tell you that if you try to write such code that you shouldn't do that, but that's all that is happening here. As far as the JVM is concerned, the String in new ClassTypeCapture<String>(Integer.class) and the Integer are not related at all, except for that one compile-time check that says: The generics aren't matching up here, so I shall generate an error. Here is an example of breaking it:
ClassTypeCapture /* raw */ a = new ClassTypeCapture<Integer>(String.class);
ClassTypeCapture<Integer> b = a;
this runs, and compiles. And b's (which is the same as a's - same reference) 'kind' field is referencing String.class. The behaviour of this object's f() method is very strange.
we don't know the type of T at runtime
You're missing the point of generics: generics allow the compiler to "sanity check" the types, to make sure they're consistent.
It's tempting to read ClassTypeCapture<T> as "a ClassTypeCapture for type T", but it's not: it's a ClassTypeCapture, with a hint to the compiler to check that all of the method invocations/field accesses/return values involving that reference are consistent with the type T.
To make this more concrete (let's do it with List, that's easier):
List<String> list = new ArrayList<>();
list.add("hello");
String e = list.get(0);
the <String> is an instruction to the compiler to do this:
List list = new ArrayList();
list.add("hello"); // Make sure this argument is a `String`
String e = (String) list.get(0); // Insert a cast here to ensure we get a String out.
At runtime, the "T-ness" isn't known any more, but the ClassTypeCapture (or Object, or String, or whatever) still is. You can ask an Object if it's an instance of an Object, String, ClassTypeCapture, or whatever.
You just can't ask it if it was a ClassTypeCapture<String> at compile time, because that <String> is just a compiler hint, not part of the type.

Understanding Principle of Truth In Advertising Java Generics

I have been trying to understand Java generics properly.So in this quest I have come accross one principle " Principle of Truth In Advertising", I am tring to understand this in simple language.
The Principle of Truth in Advertising: the reified type of an array must be a subtype
of the erasure of its static type.
I have written sample code .java and .class files as follows.Please go through code and please explain what part(in code) designates/indicates what part of above statement.
I have written comments to I think I should not write description of code here.
public class ClassA {
//when used this method throws exception
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
public static <T> T[] toArray(Collection<T> collection) {
//Array created here is of Object type
T[] array = (T[]) new Object[collection.size()];
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
//Working fine , no exception
public static <T> T[] toArray(Collection<T> collection, T[] array) {
if (array.length < collection.size()) {
//Array created here is of correct intended type and not actually Object type
//So I think , it inidicates "the reified type of an array" as type here lets say String[]
// is subtype of Object[](the erasure ), so actually no problem
array = (T[]) Array.newInstance(array.getClass().getComponentType(), collection.size());
}
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B");
String[] strings = toArray(list);
// String[] strings = toArray(list,new String[]{});
System.out.println(strings);
}
}
Please try to explain in simple language.Please point out where I am wrong. Corrected code with more comments is appreciated.
Thank you all
I refer to Java Generics and Collections as the Book and the Book's authors as the Authors.
I would upvote this question more than once as the Book makes a poor job of explaining the principle IMO.
Statement
Principle of Truth in Advertising:
the reified type of an array must be a subtype of the erasure of its static type.
Further referred to as the Principle.
How does the principle help?
Follow it and the code will compile and run without exceptions
Do not follow it and the code will compile, but throw an exception at runtime.
Vocabulary
What is a static type?
Should be called the reference type.
Provided A and B are types, in the following code
A ref = new B();
A is the static type of ref (B is the dynamic type of ref). Academia parlance term.
What is the reified type of an array?
Reification means type information available at runtime. Arrays are said to be reifiable because the VM knows their component type (at runtime).
In arr2 = new Number[30], the reified type of arr2 is Number[] an array type with component type Number.
What is the erasure of a type?
Should be called the runtime type.
The virtual machine's view (the runtime view) of a type parameter.
Provided T is a type parameter, the runtime view of the following code
<T extends Comparable<T>> void stupidMethod(T[] elems) {
T first = elems[0];
}
will be
void stupidMethod(Comparable[] elems) {
Comparable first = elems[0];
}
That makes Comparable the runtime type of T. Why Comparable? Because that's the leftmost bound of T.
What kind of code do I look at so that the Principle is relevant?
The code should imply assignment to a reference of array type. Either the lvalue or the rvalue should involve a type parameter.
e.g. provided T is a type parameter
T[] a = (T[])new Object[0]; // type parameter T involved in lvalue
or
String[] a = toArray(s); // type parameter involved in rvalue
// where the signature of toArray is
<T> T[] toArray(Collection<T> c);
The principle is not relevant where there are no type parameters involved in either lvalue or rvalue.
Example 1 (Principle followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Number[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation provides the answer: an array with component type Number is created using new. Number[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
Example 2 (Principle not followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Object[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation using new, component type is Object, therefore Object[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of Number[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 3 (Principle not followed)
Given the method providing an array
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
client code
List<String> s = ...;
String[] arr = toArray(s);
Q1: What is the reified type of the array returned by the providing method?
A1: for that you need too look in the providing method to see how it's initialized - new Object[...]. That means the reified type of the array returned by the method is Object[].
Q2: What is the static type of arr?
A2: String[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type String[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of String[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 4 (Principle followed)
Given the method providing an array
<T> T[] toArray(Collection<T> initialContent, Class<T> clazz){
T[] result = (T[]) Array.newInstance(clazz, initialContent);
// Copy contents to array. (Don't use this method in production, use Collection.toArray() instead)
return result;
}
client code
List<Number> s = ...;
Number[] arr = toArray(s, Number.class);
Q1: What is the reified type of the array returned by the providing method?
A1: array created using reflection with component type as received from the client. The answer is Number[].
Q2: What is the static type of arr?
A2: Number[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type Number[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
What's in a funny name?
Ranting here. Truth in advertising may mean selling what you state you are selling.
In
lvalue = rvalue we have rvalue as the provider and lvalue as the receiver.
It might be that the Authors thought of the provider as the Advertiser.
Referring to the providing method in Example 3 above,
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
the method signature
<T> T[] toArray(Collection<T> c);
may be read as an advertisement: Give me a List of Longs and I will give you an array of Longs.
However looking in the method body, the implementation shows that the method is not being truthful, as the array it creates and returns is an array of Objects.
So toArray method in Example 3 lies in its marketing campaigns.
In Example 4, the providing method is being truthful as the statement in the signature (Give me a collection and its type parameter as a class literal and I will give you an array with that component type) matches with what happens in the body.
Examples 3 and 4 have method signatures to act as advertisement.
Examples 1 and 2 do not have an explicit advertisement (method signature). The advertisement and the provision are intertwined.
Nevertheless, I could think of no better name for the Principle. That is a hell of a name.
Closing remarks
I consider the statement of the principle unnecessarily cryptic due to use of terms like static type and erasure type. Using reference type and runtime type/type after erasure, respectively, would make it considerably easier to grasp to the Java layman (like yours truly).
The Authors state the Book is the best on Java Generics [0]. I think that means the audience they address is a broad one and therefore more examples for the principles they introduce would be very helpful.
[0] https://youtu.be/GOMovkQCYD4?t=53
Think of it that way:
T[] array = (T[]) new Object[collection.size()]; A new Array is created. Due to language design, the type of T is unkown during runtime. In your example you know for a fact T is String, but the from the viewpoint of the vm T is Object. All casting operations are happening in the calling method.
So in toArray an array Object[] is created. The type parameter is more or less syntactic sugar which has no consequence for the bytecode created.
So why can't an array of objects be casted to an array of strings?
Let's have an example:
void methodA(){
Object[] array = new Object[10];
array[0]=Integer.valueOf(10);
array[1]=Object.class;
array[2]=new Object();
array[3]="Hello World";
methodB((String[])array);
}
void methodB(String[] stringArray){
String aString=stringArray[1]; //This is not a String, but Object.class!
}
If you could cast an array, you'd say "all elements I've added before are of a valid subtype". But since your array is of type Object, the vm can't guarantee the array will always under all circumstances contain valid subtypes.
methodB thinks it deals with an array of Strings, but in reality the array does contain very different types.
The other way around does not work either:
void methodA(){
String[] array = new String[10];
array[0]="Hello World";
methodB((Object[])array);
//Method B had controll over the array and could have added any object, especially a non-string!
System.out.println(array[1]);
}
void methodB(Object[] oArray){
oArray[1]=Long.valueOf(2);
}
I hope this helps a little bit.
Edit: After reading your question again, I think you are mixing to things:
Arrays can't be casted (as I explained above)
The cited sentence does say in plain English: "If you create an array of type A, all elements in this array must be of type A or a of a subtype of A". So if you create an array of Object you can put any java object into to array, but if you create an array of Number the values have to be of type Number (Long, Double, ...). All in all the sentence is rather trivial. Or I didn't understand it either ;)
Edit 2: As a matter of fact you can cast an array to any type you want. That is, you can cast an array as you can cast any type to String (String s=(String)Object.class;).
Especially you can cast a String[] to an Object[] and the other way around. As I pointed out in the examples, this operation introduces potential bugs in great numbers, since reading/writing to the array will likely fail. I can think of no situation where it is a good decision to cast an array. There might be situations (like generalized utility classes) where it seems to be a good solution, but I still would suggest to overthink the design if you find yourself in a situation where you want to cast an array.
Thanks to newacct for pointing out the cast operation itself is valid.

Array and reified type [duplicate]

This question already has answers here:
What's the reason I can't create generic array types in Java?
(16 answers)
Closed 7 years ago.
I'm studying for my Java midterm but I have some problems with the reified type. Here there is a class that is wrong, but I cannot understand why. Can someone help me and maybe give me some explanation? The error is, of course, related to the reified type.
class Conversion {
public static <T> T[] toArray(Collection<T> c) {
T[] a = new T[c.size()];
int i = 0;
for (T x: c) a[i++] = x;
return a;
}
}
An array is a reified type. This means that the exact type of the array is known at runtime. So at runtime there is a difference between, for example, String[] and Integer[].
This is not the case with generics. Generics are a compile-time construct: they are used to check the types at compile-time, but at runtime the exact types are not available anymore. At runtime, the type parameters are just Object (or if the type parameter has an upper bound, its upper bound). So at run-time, there is no difference in the type of Collection<String> and Collection<Integer>.
Now, there is a problem when you want to create an array of a type parameter. At runtime it is unknown what T is, so if you write new T[10], the Java runtime doesn't known what kind of array is to be created, a String[] or a Integer[]. That's why you cannot create an array in this way.
There are a few work-arounds, none of which is entirely satisfactory. The usual solution is to create an Object[], and cast it to the kind of array you want:
T[] theArray = (T[]) new Object[size];
However, you have to remember that this is very unsafe. You should only do this if the scope of the created arrow is small, so that you can manually make sure that the array will only contain T instances and will never be assigned to anything that cannot hold it. The following code demonstrates the problem:
public class Foo<T extends Comparable> {
T[] createArray() {
return (T[])new Object[1];
}
public static void main(String... args) {
Foo<String> foo = new Foo<>();
String[] ss = foo.createArray(); // here
}
}
The line marked with here throws an exception, because you are trying to cast an Object[] to a String[]!
If you really need an array of the correct run-time type, you need to use reflection. Obtain a type token (of type Class<T>) of the type you need, and use Array.newInstance(type, cize) to create the array, for example:
public T[] createArray(Class<T> type, int size) {
return (T[]) Array.newInstance(type, size);
}
Reifiable type is defined by the JLS as:
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
It is a raw type (§4.8).
It is a primitive type (§4.2).
It is an array type (§10.1) whose element type is reifiable.
It is a nested type where, for each type T separated by a ".", T itself is reifiable.
See also the notes that follow §4.7, the reasoning is described in great detail.
Your type parameter is none of the above, therefore it is not reifiable. And thus can't be used in an array creation expression:
It is a compile-time error if the ClassOrInterfaceType does not denote
a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name
any named reference type, even an abstract class type (§8.1.1.1) or an
interface type.
You can't create an array of a generic type. The compiler likely complains with something like "generic array creation". There's no nice way around this, but there is a way to do this:
public static <T> T[] toArray(Class<T> type, Collection<T> c) {
T[] a = (T[]) Array.newInstance(type, c.size())
…
}
You'll need Class<T> for this, but it does work :)

Casting with NewInstance

I need to make cast from one type to another. Googled but stucked. Say I want to cast initObject to Casted class.
Object objInstance = initObject.getClass().newInstance();
Casted str=(Casted)objInstance;
Why it make ClassCastException?
Casting requires that Class Casted must be a subclass or subinterface of Class initObject. For example:
List<String> list = new ArrayList<String>();
ArrayList<String> castedList = (ArrayList<String>) list; //works
Integer int = (Integer) list; // throws ClassCastException
An alternative to casting could be a helper method that will convert related objects. Such an example is the Collections.toArray method.
Casting is usually discouraged as you are basically telling the compiler that you know what type the casted object is, and of course you could be wrong.

Are generic type parameters converted to Object for raw types?

Consider this piece of code:
public class Main {
public static void main(String[] args) {
Cat<Integer> cat = new Cat();
Integer i= cat.meow();
cat.var = 6;
}
}
public class Cat<E> {
public E var;
public E meow() {
return null;
}
}
As per my understanding since I've not specified the type parameter on LHS, it would be taken as Object. And Cat should become Cat<Object> because for the variable declaration to make any sense T must translate to a Class/Interface reference. Is this a correct understanding of how it works? How is type parameter T handled in case of raw types?
I've discussed this on chat and got following explanation which is way over my head:
Generics works because types are erased. Consider T an erasure of
#0-capture-of-Object. When T isn't specified (rawtyped), it is #0-capture-of-(nothing)
What does #0-capture-of-(nothing) mean?
Side note: since generic types are implemented as compile time transformations it would be easier to understand them if one could see the final translated code. Does anyone know a way to see the translated code (not byte code) for a class?
No,
raw types are not as if they are paramterized with Object, nor are they like wildcard types (<?>).
For raw types, generics are turned off.
This code is compiles (with warnings):
Cat c1 = new Cat<String>();
Cat<Integer> c2 = c1;
This code does not:
Cat<? extends Object> c1 = new Cat<String>(); // btw: this is the same as Cat<?>
Cat<Integer> c2 = c1; // error
neither does this:
Cat<Object> c1 = new Cat();
Cat<Integer> c2 = c1; // error
As for the type of var:
the type of the field after compilation is whatever the upper-bound of the parameter is (Object if none is specified). But what does the compiler do if we access var?
Cat<String> c1 = ...
String c1Var = c1.var;
This code compiles without error, but what the compiler will actually compile is this:
Cat c1 = ...
String c1Var = (String) c1.var;
As you can see, var is always treated as a field of type Object, but with generics, the compiler automatically inserts type-safe casts. That's all. If you use raw types, you have to do it yourself. Either way, when you put a Cat that stores an integer in a Cat<String> variable, you will only get a runtime exception if you try to read var.
A quiz
Now look at the declaration of Collections.max. Why do you think the parameter is defined as T extends Object & Comparable<? super T>?
Answer encoded in rot13:
Fb gung nsgre pbzcvyngvba gur erghea glcr vf Bowrpg, abg Pbzcnenoyr. Guvf vf arrqrq sbe onpxjneqf pbzcngvovyvgl (gur zrgubq vgfrys vf byqre guna trarevpf).
Edit:
Here is another good example that I just stumbled upon:
class Foo<T> {
public <V> V bar(V v) { return v; }
}
//compiles
Foo<Object> foo = new Foo<Object>();
Integer i = foo.bar(1);
//compiles
Foo<?> foo = new Foo<String>();
Integer i = foo.bar(1);
// fails
Foo foo = new Foo();
Integer i = foo.bar(1); // error: Object cannot be converted to Integer
Using no parameters disables generics entirely.
This code is valid:
Cat c = new Cat<Integer>();
c is now of the Raw Type Cat.
This is not valid:
Cat<Object> c = new Cat<Integer>(); // Compiler error
So, it's not exactly the same. Though you can, after the first line, do things like:
c.var = 5;
System.out.println(c.var);
c.var = 1;
System.out.println(c.var);
c.var = "test";
System.out.println(c.var);
Outputs:
5
1
test
#Cephalopod has provided the correct answer, however I'd like to expand on that with some of my own explanation.
for the variable declaration to make any sense T must translate to a Class/Interface reference.
That is correct. Generics are a compile time transformation. Runtime system has no notion of abstract types. So before the class is loaded into memory the abstract type T must be replaced by an actual type reference.
Run the following code:
System.out.println(Cat.class.getMethod("meow").getReturnType());
System.out.println(Cat.class.getField("var").getType());
The output is:
class java.lang.Object
class java.lang.Object
The formal type parameter E has been replaced with Object.
Cat should become Cat<Object>
Wrong. Cat will stay Cat. Why? Look at the decompiled class file for Main:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Integer i = (Integer)cat.meow();
cat.var = Integer.valueOf(6);
}
}
The purpose of specifying formal type parameter with <> is to enable compiler to generate explicit casts.
When you say new Cat() it doesn't have to turn into anything, the compiler simply won't generate a cast and the method call would look like:
Integer i = cat.meow(); // No cast at all
Are generic type parameters converted to Object for raw types?
To clarify what is being asked here, the above questions means: Is E replaced with java.lang.Object if I don't specify anything when instantiating Cat.
Actually E would be replaced with java.lang.Object even if you specified <Integer> when instantiating Cat. The replacement/transformation is done at compile time while the instantiation is at runtime. How you use the type isn't going to change its class definition.
Generic types defined in objects like the
Cat c = new Cat<Integer>();
are only intended to provide the compiler with the chance to check that the types will match at runtime.
Generic types assigned in class definitions are retained in the compiled class.
public class Cat<T extends Number>{}
Or
public class Intcat extends Cat<Integer>{}
The runtime knows that the generic argument is bound by Number in the first case and is Integer in the first case.
I have no links to back this up, but I'd rather assume that c becomes raw type Cat, not Cat<Object>.
Raw types don't handle parameter T, which is why they are prone to errors.
javadoc says: A raw type is the name of a generic class or interface without any type arguments.
That chat log seems to mean exactly that, but in a confusing manner.
I actually Do not know How it is actually implemented in the bytecode But from my understanding Cat c = new Cat<Integer>(); Stores the new instance of Cat created by new Cat<Integer>() in the variable c. Now if you query c to know what is the type of var it will reply Integer and not Object because the instance that was created has a type of Integer.
Now If you execute c.var = "Text"; and query c to know what is the type of var. It would reply String. This does not means that by default it is converting <T> to Object. It means that c does not know what is the type of var.
I feel that is why the <?> wild card is used. Cat<?> c = new Cat<Integer>(); it would always convert <T> to Object. That is the reason why it is always advised not the use raw types for generics.
I think Cat c is a RAW type and could be considered as a "wildcard type" like Cat<?>. Since Cat<?> is the supertype of each type of Cat including Cat<Integer>, Cat c may take a new Cat<Integer> object.
This is also mentioned here: Interoperating with Legacy Code
"Most people's first instinct is that Collection really means Collection. However, as we saw earlier, it isn't safe to pass a Collection in a place where a Collection is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection."
...
"So raw types are very much like wildcard types, but they are not typechecked as stringently. This is a deliberate design decision, to allow generics to interoperate with pre-existing legacy code."

Categories