array of strings vs array of objects java - java

I came across this section of commented code on StackOverflow. It will throw a Runtime exception. The second part (the un-commented section) will work. I am not able to find the difference between the two, as both methods are returning arrays of Objects. Maybe I am missing some basic understanding. Can you please help me?
public class Safevarargs {
/*
static <Object> Object[] asArray(Object... args) {
return args;
}
static <Object> Object[] arrayOfTwo( Object a, Object b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
*/
static <Object> Object[] display(Object... args) {
return args;
}
public static void main(String[] args) {
String[] str = display("hi", "mom");
System.out.print(str[0]);
}
}

I believe you are having a problem with casting. When you use the part
static <Object> Object[] display(Object... args)
what the compiler is actually seeing is
static <Object> Object[] display(Object[]... args)
then it further converts it to this
static <Object> Object[] display(List[]... args)
This might allow values that shouldn't be in there
lets say you make an object array of string and try adding an int to it. This would causes a problem with the different types. You might want to rework it without using the generic Object.
Alternatively you could use the #SafeVarargs tag.

To explain this issue, we just need to know how the generic work in java. the compiler uses the generic type information internally during the process of compilation, generating type-related errors as it processes the sources. Then, once the validation is done, the compiler generates type-erased byte code, with all references to generic types replaced with their respective type erasure.
in your second example, the it is compile to string during the compile time. but in your first example, since the asArray is called by another generic method, it will be compile into Object during compile time. As a result, in the running time, the Object[] can not be cast to String[] which will throw class cast exception. hope this can help you

This is an interesting situation here. In the first instance, Java complains about casting from an object array to a string array.
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
In the second case, it handles it just fine. The issue seems to be type erasure. When asArray returns, it's returning an array typed based on its parameters. That information is erased within the context of arrayOfTwo, where the type simply becomes Object[], and the fact that the contents are strings is no longer available.
Have a look at this post on type erasure and non-reifiable types, and the implications on varargs. It does provide some context on the warning which the code generates, about "possible heap pollution":
Safevarargs.java:11: warning: [unchecked] Possible heap pollution from parameterized vararg type Object
static Object[] display(Object... args) {
^
where Object is a type-variable:
Object extends java.lang.Object declared in method display(Object...)
From the documentation:
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type.

Related

Creating an array of generics in java in the method, without a class object

I am doing tasks from the Java SE 8 book by Cay S. Horstmann (Chapter 6 task 21)
Using the #SafeVarargs annotation, write a method that allows you to build arrays of generalized types, as in the following example:
List<String>[] result = Arrays.<List<String> > construct (10) ;
// Sets the result in a list of type List<String>[] with a length of 10
My intended solution was such
static <T> T[] construct(int size) {
ArrayList<String> arr = new ArrayList<>();
for (int i = 0; i < size; i++) arr.add(null);
return (T[]) arr.toArray();
}
but it is incorrect since after erasing occurs
Exception in thread "main" java.lang.ClassCastException: class
[Ljava.lang.Object; cannot be cast to class [Ljava.util.List;
([Ljava.lang.Object; and [Ljava.util.List; are in module java.base of
loader 'bootstrap') at Main.main(Main.java:23)
Don’t use this in production code. This is only for answering the book’s question as-is, for educational purpose. See the end of the answer for details.
The #SafeVarargs does already provide a hint towards the intended solution. Make the method a varargs method. Since a variable number of arguments also allows zero arguments, this change allows unchanged invocations:
public class Example
{
#SafeVarargs
static <T> T[] construct(int size, T... template) {
return Arrays.copyOf(template, size);
}
public static void main(String args[])
{
List<String>[] result = Example.<List<String>>construct(10);
System.out.println(result.getClass().getComponentType());
}
}
Note that you don’t need explicit type parameters here. The caller can be simplified to
List<String>[] result = Example.construct(10);
which implies that we can even omit the declaring class when being in the same class or using import static …
List<String>[] result = construct(10);
But when the caller does provide arguments to the varargs parameter, they may show up in the resulting array. To enforce an array with only null elements, you could use
#SafeVarargs
static <T> T[] construct(int size, T... template) {
return Arrays.copyOf(Arrays.copyOf(template, 0), size);
}
or
#SafeVarargs
static <T> T[] construct(int size, T... template) {
T[] array = Arrays.copyOf(template, size);
Arrays.fill(array, null);
return array;
}
which can be tested with
List<String>[] result = construct(10, Collections.emptyList());
System.out.println(result.getClass().getComponentType());
System.out.println(Arrays.toString(result));
But as said, don’t use this code. The creation of generic arrays is not allowed for a reason. Consider
List<String>[] result = construct(10);
Object[] array = result;
array[0] = Arrays.asList(42);
List<String> stringList = result[0];
// this situation is called heap pollution
// at an entirely different place in you application, you might do
String s = stringList.get(0);
// without understanding what's going on
The reason why this code does not produce a warning, is that the #SafeVarargs prevents this and is used incorrectly here. For a varargs method to be safe, we must prevent any assignment to the array (this applies to copies of the array as well), but as soon as we return a generic array, we can’t guaranty this. As the user newacct correctly pointed out, there’s not even a guaranty that the array’s raw element type matches the intended type, e.g. if the caller also uses a type variable for T. If this has an impact on a method’s behavior, it must not use the #SafeVarargs annotation.
It's a common mistake. arr.toArray() returns Object[] and then you try to cast it to List[] array. Unlike generics the type of arrays is not removed during runtime. That's why you get a class cast exception.
It's not possible to write such a method in Java that will work in all circumstances, and I can prove it. The mention of #SafeVarargs indicates that they want you to use varargs, and the varargs semantics does allow you to do some interesting things, but it will still not allow you to write such a method that works in all circumstances.
When you have a varargs method in Java like someMethod(int size, String... args), it receives a second argument of type String[], and when you call it with (0 or more) String arguments (and not a String[] argument directly), the compiler will implicitly create a new array like new String[] { ... } and pass that to the method under the hood.
The varargs parameter can also be of type variable type like someMethod(int size, T... args). If you call this method with 0 or more T arguments, the compiler will also try to implicitly create an array of the arguments. If the compiler knows (whether by inference or you explicitly specified) that for this call T is a reifiable type like String, then it will do new String[] { ... } with no problems.
However, when the compiler's (inferred or explicitly specified) T for this call is a parameterized type or a type variable, there is a problem because Java does now allow creating an array of a parameterized type (e.g. new List<String>[...]) or an array of a type variable type (e.g. new U[...]). What Java does is implicitly create an array with the component type being the erasure of what it inferred T to be. So if T for this call is List<String>, it will create new List[] { ... }, and if T for this call is U, then it will create an array of the upper bound of U; so if U is unbounded, this would be new Object[] { ... }. It will then produce a warning to indicate that this is unsafe.
The reason why this warning is necessary is that the runtime type of the varargs array object received in the method is not compatible with the parameter's declared type (this is called "heap pollution"). In the case where the expected type is an array of a parameterized type (e.g. List<String>[]) and it creates an array of the raw type (e.g. List[]), the difference is mostly not a big deal (although there are still some reasons why a warning is a good idea that I won't go into), and in fact people often use new List[...] as a workaround when needing to create an array of a parameterized type. However, in the case where the expected type is an array of a type variable type (e.g. U[]), creating an array of the upper bound type (e.g. Object[]) is potentially a big problem, because if this is returned to a scope which expects U to be a specific type (e.g. String), then it will expect U[] to be String[], and an array of runtime type Object[] will not be able to be put into that type since String[] and Object[] are actually separate classes at runtime, and Object[] is not a subclass of String[].
For most varargs methods that take T[], they just iterate through the varargs arguments, and they just care that the elements are of type T, and don't care that the runtime type of the varargs array object is T[]. In such cases, Java provides a #SafeVarargs annotation that can be put on the method to suppress this warning. However, in varargs methods that do care about the runtime type of the varargs array object, you should not use this annotation, because it suppresses the warning that warns of a real problem.
#Holger's answer, which is probably what the book's author was thinking of, takes advantage of varargs' implicit array creation. Even in the case of 0 variable arguments, the compiler still implicitly creates an array of the right type, and the array carries runtime information about its component type, which the method can try to use to create the array to return. So in the case of Arrays.<List<String>>construct(10), even though it doesn't look like you are passing any information in at runtime about the component type of the array, you actually are because the compiler implicitly passes a new List[] {}, and this carries the type List into the method at runtime. (Again, there is a difference between List and List<String>, but the difference is minor, and in most cases, you are not going to run into problems with this.) But this only works because the component type (List) is known at compile time, so the compiler can hard-code it at compile-time.
If you instead call this method where the component type is not known at compile time, i.e. is a type variable like Arrays.<U>construct(10), then the only thing that the compiler can send into it is new Object[], i.e. the only type information that can be passed into the method at runtime is the upper bound of the type variable (because that is known at compile time), but not the actual type that the type variable represents. The method can then only create an array with runtime type of Object[], which is incompatible with the right type of U[] (unless U happens to be Object). As mentioned above, the compiler usually generates a warning when this happens, but the book suggests you to use #SafeVarargs to suppress this warning, so you wouldn't get a warning. (And #SafeVarargs is not suitable for such a method, because it does use the runtime type of the varargs array object to create its new array.)
We can extend this further to create a proof by contradiction that the method that the question asks for is impossible in Java. We assume that there does exist a way to write the method, and assume that we have the working implementation of construct(), that is annotated with #SafeVarargs. Then we can use it to write this method:
public static <U> U[] foo(int size) {
return construct(int);
}
This method takes a size, and returns an array of the desired component type with that size, without needing to be passed any information at runtime about what that component type is. This method does not use varargs, so there is no varargs shenanigans like implicit arguments under the hood. This is just a plain non-varargs method that just takes one parameter, the size. It compiles no warnings (since construct() is annotated with #SafeVarargs). It is very obvious that this method is impossible in Java. To create an array in Java, we need to provide the component type at runtime, and this method purports to magically be able to create an array of any type you want, without you telling it what type it is that you want at runtime. This is basically creation of an array of type variable type (i.e. new U[...]), and we know that this is not possible. Since this is a contradiction, that proves that the assumption that there exists a way to write the construct() method, is false.
If you limit the circumstances that the method has to work in to only those where the component type is a reifiable type or parameterized type, and not a type variable type, then #Holger's answer would qualify. But 1) that was not stipulated in the question from the book, and 2) such a restriction would go against the whole spirit of generics, since a generic method should work just as well no matter if it's called with T being a concrete type or type variable -- it shouldn't care at all.

"Potential heap pollution via varargs parameter" for Enum<E>... why?

This question is specific to using varargs with generic-fied Enum<E>s:
Why do I get this warning Type safety: Potential heap pollution via varargs parameter elements if I define the method the like this:
<E extends Enum<E>> void someMethod(E... elements)
As opposed to this:
<E extends Enum<E>> void someMethod(E[] elements)
Accordingly, what should I look out for before declaring the method #SafeVarargs?
Similar questions
This question is similar to these questions regarding Collection<T>... but the scenarios shown in those answers do not seem to apply with Enum<E>...:
Potential heap pollution via varargs parameter
Type safety: Potential heap pollution via varargs parameter subtrees
Potential heap pollution via varargs parameter
Eclipse different behaviour for "Unchecked" warning due to "Potential heap pollution via varargs parameter". How to fix?
It is the reverse of this question which questions why there are no warning:
Why doesn't the following method produce potential heap pollution?
Sample code
This is what I tried to pollute the heap, but each bad attempt results in java.lang.ArrayStoreException and not a polluted array.
I am using Eclipse 4.6.0 and Java JDK 8u74.
public static void main(String[] args) {
Foo[] x = { Foo.A };
someMethod(x);
Foo y = x[0]; // How does one get a ClassCastException here?
}
private static enum Foo {
A, B, C,
}
private static enum Bar {
X, Y, Z,
}
// This produces a "Type safety" warning on 'elements'
private static <E extends Enum<E>> void someMethod(E... elements) {
Object[] objects = elements;
// Test case 1: This line throws java.lang.ArrayStoreException
objects[0] = "";
// Test case 2: Program terminates without errors
objects[0] = Foo.A;
// Test case 3: This line throws java.lang.ArrayStoreException
objects[0] = Bar.X;
}
There is a warning for the varargs method because varargs methods can cause implicit array creation at the calling site, whereas the version that takes an array parameter doesn't. The way that varargs works is that it causes the compiler to create an array at the call site filled with the variable arguments, which is then passed to the method as a single array parameter. The parameter would have type E[], so the array created should be an E[].
First, in the call site in your example, you are not using the variable arguments functionality at all. You are passing the array of variable arguments directly. So there is no implicit array creation in this case.
Even if you had used the variable arguments functionality, e.g. with someMethod(Foo.A);, the implicit array creation would be creating an array with a reifiable type, i.e. the variable argument type is known at the call site to be Foo which is a concrete type known at compile-time, so array creation is fine.
The problem is only if the variable argument type at the call site is also a generic type or type parameter. For example, something like:
public static <E extends Enum<E>> void foo(E obj) {
someMethod(obj);
}
Then the compiler would need to create an array of this generic type or type parameter (it needs to create an E[]), but as you know generic array creation is not allowed in Java. It would instead create an array with the component type being the erasure of the generic type (in this example it would be Enum), so it would pass an array of the wrong type (in this example, the passed array should be an E[], but the array's actual runtime class would be Enum[] which is not a subtype of E[]).
This potential wrong type of array is not always a problem. Most of the time, a varargs method will simply iterate over the array and get elements out of it. In this case, the runtime class of the array is irrelevant; all that matters is that the elements are type E (which indeed they are). If this is the case, you can declare your method #SafeVarargs. However, if your method actually uses the runtime class of the passed array (for example, if you return the varargs array as type E[], or you use something like Arrays.copyOf() to create an array with the same runtime class), then a wrong runtime class of the array will lead to problems and you cannot use #SafeVarargs.
Your example is a bit weird because, well, you are not even using the fact that the elements are of type E, let alone the fact that the array is E[]. So you could use #SafeVarargs, and not only that, you could simply declare the array as taking Object[] in the first place:
private static void someMethod(Object... objects)

java warning: Varargs method could cause heap pollution from non-reifiable varargs parameter

I am using IntelliJ IDEA with javac on JDK 1.8. I have the following code:
class Test<T extends Throwable>
{
#SafeVarargs
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
}
}
IntelliJ IDEA does not highlight anything in the above code as a warning. However, when compiling, the following line appears in the "Make" tab of the "Messages" view:
Warning:(L, C) java: Varargs method could cause heap pollution from
non-reifiable varargs parameter varargs
Note #1: I have already specified #SafeVarargs.
Note #2: Warning:(L,C) points to varargs being passed as an argument to arrayMethod()
Assuming that I know what I am doing, and supposing that I am pretty sure that there will be no heap pollution or that I promise that I will not call this method in some funky way which might result in heap pollution, what do I need to do to suppress this warning message?
NOTE: There is a multitude of questions on stackoverflow regarding varargs methods, but it appears that there is none that addresses this specific problem. As a matter of fact, the entire interwebz appear to be rather poor in answers to this particular question.
None of the answers I've seen on this question seem to me to be satisfactory so I thought I'd take a stab at it.
Here's the way I see it:
#SafeVarargs
Suppresses the warning: [unchecked] Possible heap pollution from parameterized vararg type Foo.
Is part of the method's contract, hence why the annotation has runtime retention.
Is a promise to the caller of the method that the method will not mess up the heap using the generic varargs argument.
#SuppressWarnings("varargs")
Suppresses the warning: [varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar.
Is a cure for problems occurring within the method code, not on the method's contract, hence why the annotation only has source code retention.
Tells the compiler that it doesn't need to worry about a callee method called by the method code messing up the heap using the array resulting from the non-reifiable varargs parameter.
So if I take the following simple variation on OP's original code:
class Foo {
static <T> void bar(final T... barArgs) {
baz(barArgs);
}
static <T> void baz(final T[] bazArgs) { }
}
The output of $ javac -Xlint:all Foo.java using the Java 9.0.1 compiler is:
Foo.java:2: warning: [unchecked] Possible heap pollution from parameterized vararg type T
static <T> void bar(final T... barArgs) {
^
where T is a type-variable:
T extends Object declared in method <T>bar(T...)
1 warning
I can make that warning go away by tagging bar() as #SafeVarargs. This both makes the warning go away and, by adding varargs safety to the method contract, makes sure that anyone who calls bar will not have to suppress any varargs warnings.
However, it also makes the Java compiler look more carefully at the method code itself - I guess in order to verify the easy cases where bar() might be violating the contract I just made with #SafeVarargs. And it sees that bar() invokes baz() passing in barArgs and figures since baz() takes an Object[] due to type erasure, baz() could mess up the heap, thus causing bar() to do it transitively.
So I need to also add #SuppressWarnings("varargs") to bar() to make that warning about bar()'s code go away.
In fact you should not write your code in this way. Consider the following example:
import java.util.*;
class Test<T extends Throwable>
{
#SafeVarargs
#SuppressWarnings("varargs")
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
Object[] array = args;
array[1] = new Integer(1);
//
//ArrayList<Integer> list = new ArrayList<>();
//list.add(new Integer(1));
//array[1] = list;
}
public static void main(String[] args)
{
ArrayList<Exception> list1 = new ArrayList<>();
ArrayList<Exception> list2 = new ArrayList<>();
(new Test<Exception>()).varargsMethod(list1, list2);
}
}
If you run the code, you will see an ArrayStoreException because you put an Integer into a Collection<T> array.
However, if you replace array[1] = new Integer(1); with the three comment lines (i.e. to put an ArrayList<Integer> into the array), due to type erasure, no exception is thrown and no compilation error occurs.
You want to have a Collection<Exception> array, but now it contains a ArrayList<Integer>. This is quite dangerous as you won't realise there is a problem.
An additional (and quite superfluous-looking) #SuppressWarnings( "varargs" ) is needed to suppress the warning, as follows:
#SafeVarargs
#SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
Assuming that I know what I am doing
I refuse to assume this since this seems incredibly wrong.
The situation you're running into here is that, because generics are not reifiable, you're declaring a generic array, which is generally frowned upon. Generics and arrays don't mix very well, given that an array is covariant (String[] is an Object[] in the same way that a String is an Object), whereas generics are invariant (List<String> is not a List<Object>, even though a String is an Object).
If you want a collection of collections...just pass one. It's safer than mingling arrays and generics.
final void varargsMethod(Collection<<Collection<? super T>> collections) { }
This might explain the reason why. I just copied it from Effective Java 2nd Edition, Item 25. Hopefully, it can help.
The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic type to return an array of its element type (but see Item 29 for a partial solution). It also means that you can get confusing warnings when using varargs methods (Item 42) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. There is little you can do about these warnings other than to suppress them (Item 24), and to avoid mixing generics and varargs in your APIs.

Why does giving explicit type arguments to a non-generic method or constructor compile?

When instantiating ArrayLists I am used to seeing code like this
ArrayList<Type> arr = new ArrayList<Type>();
or
ArrayList<Type> arr = new ArrayList<>();
however today I have come across an instantiation of ArrayList that looks like this:
ArrayList<Type> arr = new <Type>ArrayList();
what is going on, and why does that give an "unsafe operations" compile warning?
Edit:
Yes, found the reference. See JLS §15.12.2.1 - Identify Potentially Applicable Methods:
If the method invocation includes explicit type arguments, and the
member is a generic method, then the number of type arguments is equal
to the number of type parameters of the method.
This clause implies that a non-generic method may be potentially
applicable to an invocation that supplies explicit type arguments.
Indeed, it may turn out to be applicable. In such a case, the type
arguments will simply be ignored.
Emphasis mine.
Also see JLS §15.9.3 - Choosing the Constructor and its Arguments, for understanding how the constructor invocation is resolved. It also mentions that the above mentioned process is followed for resolution.
Original answer:
Such kind of invocation is often required, when you have a generic constructor, and the compiler is not able to infer the correct type arguments. For example, consider the below code:
class Demo<T> {
public <X> Demo(X[] arg1, X arg2) {
// initialization code
System.out.println(arg1.getClass());
System.out.println(arg2.getClass());
}
}
Suppose you invoke that constructor like this:
Demo<String> demo = new Demo<String>(new String[2], new Integer(5));
You would think that the type inference should fail, as the type arguments should have same types. Here we're passing String and Integer types. But it doesn't. The compiler infers the type X as:
Object & Serializable & Comparable<? extends Object&Serializable&Comparable<?>>
Now, you might want the type parameter to be inferred as just Object, then in that case, you can provide explicit type arguments, as in the below code:
Demo<String> demo = new <Object>Demo<String>(new String[2], new Integer(5));
This is similar to how you give explicit type argument while method invocation.
Now, in your code, you have given the explicit type arguments, but you're using raw type of the class to instantiate it:
ArrayList<Integer> arr = new <String>ArrayList();
The <String> is the explicit type argument for the constructor, and compiler will be fine with it. But the issue is, you're instantiating raw type ArrayList, and that is where compiler is giving your unchecked warning. If you change that code to:
ArrayList<Integer> arr = new <String>ArrayList<>();
The warning will go away. But since ArrayList constructor is not a generic constructor, the type argument seems to be just ignored by the constructor. In fact there is no use of that type argument there.
Strangely enough, this also compiles:
public static void test() { }
public static void main(String... args) {
Main.<Integer>test();
}
...even though test() is a non-generic method.
I've just tried:
ArrayList<Integer> arr = new <String>ArrayList();
And got the same warning (not an error!). Looks like the compiler ignores1 the generics after the new keyword and before the ArrayList. It's just like writing:
ArrayList<Integer> arr = new ArrayList();
1 I'm not sure if it really "ignores" that, I'll be glad if someone confirms/correct me
The code, it does nothing!
int a = new <String>Integer(5);
It also compiles but generates a warning of "unused generics".
So basically it is useless but not bad enough to generate an error by default it seems. Either way your arraylist is NOT properly generified here.
Please note that generics are compiled away anyway so at the bytecode level it probably won't look any different. I have looked in eclipse for a way to turn this into an error instead of warning but no luck there.
UPDATE
This answer boils down to the same as the other one which is currently at +5, so why is mine downvoted? Please leave a comment if you downvote.

List vs List<Object>

Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?
EDIT: I found that the following gives a compilation error
public class TestClass
{
static void func(List<Object> o, Object s){
o.add(s);
}
public static void main(String[] args){
func(new ArrayList<String>(), new Integer(1));
}
}
whereas this doesn't
public class TestClass
{
static void func(List o, Object s){
o.add(s);
}
public static void main(String[] args){
func(new ArrayList<String>(), new Integer(1));
}
}
Why?
List is a list of some type you don't know. It could be a List<String>, List<Integer>, etc.
It's effectively equivalent to List<?>, or List<? extends Object>, except that it doesn't document that fact. It's only supported for backwards compatibility.
List<Object> is a list of Objects. Any object of any type can be put inside it, contrary to a List<String>, for example, which only accepts strings.
So no, they're not the same thing.
Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?
No they are not the same thing.
If you are providing an API,
class API {
static List<Object> getList() { ... }
static void modifyList(List<Object> l) { ... }
}
and a client uses it improperly
List<Integer> list = API.getList();
API.modifyList(list);
for (Integer i : list) { ... } // Invalid
then when your API specifies List<Object> they get a compile-time error, but they don't when API.getList() returns a List and API.modifyList(list) takes a List without generic type parameters.
EDIT:
In comments you mentioned changing
void func(List<Object> s, Object c) { s.add(c); }
to
void func(List s, Object c) { s.add(c); }
so that
func(new List<String>(), "");
would work.
That is violating type safety. The type-safe way to do this is
<T> void func(List<? super T> s, T c) { s.add(c); }
which is basically saying that func is a parameterized function that takes a List whose type can be any super class of T, and a value of type T, and adds the value to the list.
A List<Object> isn't really any more typesafe than a List. However, the Object in the code does imply intent. When someone else looks at it later, they can see that you purposefully chose Object as the type, rather than wondering if you just forgot to put a type or are storing something else and typecasting it elsewhere.
Since code gets read more than it gets written, hints at the intent of the code can be very valuable later on.
The reason you have a compiler warning when you use List instead of List<Object> is that when you have a List the compiler doesn't know what type of List it is, so while you could treat it as a List<Object>, the compiler can't ensure that at some other point in the code it wasn't set to reference a List<String> and the type safety of the Generics cannot be checked. That is really the compiler warning here - it is saying it can't help ensure the type safety of the generics, and it won't happen at runtime either (until at some later point there is an actual cast in the code).
For purposes here, you could say they're the same thing. But presumably you almost never actually fill a List with pure instance of Object. They're Strings or something. In this example, List<Object> is technically using generics but not really taking any advantage of it. So, it loses the compile-time type checking of generics.
Type Erasure is one answer and the backward compatibility to pre Java 1.5 and tighter type check in case of first one.
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

Categories

Resources