Shiv
Shiv

Reputation: 541

Trying to understand Generics

I have the following piece of code

public static void main(String[] args) {
        ArrayList<Integer> iList = new ArrayList();
        iList = returList();
        for (int i = 0; i < iList.size(); i++) {
            System.out.println(iList.get(i));
        }
    }

    public static ArrayList returList() {
        ArrayList al = new ArrayList();
        al.add("S");
        al.add(1);
        return al;
    }

now my query is why the Arraylist is accepting the raw arraylist object creation in line 'ArrayList iList = new ArrayList();' and the same case even from the method call return even.

Now, which type of data will be will be there and will Generics implies ? i see no compilation errors and this code is running fine even.

Upvotes: 0

Views: 158

Answers (6)

matthewSpleep
matthewSpleep

Reputation: 260

Because of the way Java generics are implemented (see Type Erasure for an introductory explanation) it is possible to create 'raw' type instances of generic classes and then cast them to a generic version as in your assignments to iList. This results in a compiler warning as it is a potentially unsafe operation. You can add any type you like to a raw ArrayList (it's equivalent to ArrayList) but if you then cast it to a more specific generic type you may have an inconsistent collection.

In your example you have created such a list in returList. However your code doesn't exhibit this as println() doesn't rely on the type of the list element passed to it. Add a line such as

Integer val = iList.get(i);

to your for loop and run your code and you will get a ClassCastException as your programme attempts to cast the string "s" to an Integer.

When a generic collection is inconsistent you you won't find out until you try to access the inconsistent elements using the generic type.

Upvotes: 1

sw1nn
sw1nn

Reputation: 7328

A bit of the history of generics might help explain some of the oddities.

When generics were initially proposed for Java, the assumption was that the changes would have to be introduced without changing the underlying .class format. So generics were implemented as a thin layer over the original types. A technique called type erasure became the solution. What this means is that the compiler removes all the generics information such that at runtime non of the generic information is available.

This has annoying consequences where you can't rely on the types as shown in your source code.

This is what's happening in your example.

See http://docs.oracle.com/javase/tutorial/java/generics/erasure.html for more details.

Note that for Java 5 the .class format was eventually changed to accommodate auto-boxing. The fact that the class format was changing, thus allowing generics to be done properly wasn't taken advantage of because of time constraints.

Upvotes: 0

Perception
Perception

Reputation: 80603

Generics were created as an aid to the developer, but you have to use them in order to benefit. Your IDE will have warned you of your raw usage of ArrayList:

ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized

But it does not prevent you from hanging yourself, no. Note that in some IDE's you can actually force these to be compile errors, which may be what you are looking for.

Now as far as what is in your list, it is exactly what you put in it. Remember that generics are little more than 'syntactic sugar', compiler hints that are removed completely from generated classes. So at run time there is no indication of what generic type an object had. In your case, your code works fine because all you are doing is printing out the list contents. All your objects are getting automatically converted to Strings! Try this instead for some fun and games:

public static void main(String[] args) {
        ArrayList<Integer> iList = new ArrayList();
        iList = returList();
        for (final Integer i: iList) {
            System.out.println(i.intValue());
        }
}

Upvotes: 1

Rostislav Matl
Rostislav Matl

Reputation: 4543

Read this: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf , two or three times is recommended.

Upvotes: 1

Aram Kocharyan
Aram Kocharyan

Reputation: 20431

This is answered here:

Why are raw types permitted?

Raw types are permitted in the language predominantly to facilitate interfacing with non-generic (legacy) code.

If, for instance, you have a non-generic legacy method that takes a List as an argument, you can pass a parameterized type such as List to that method. Conversely, if you have a method that returns a List , you can assign the result to a reference variable of type List , provided you know for some reason that the returned list really is a list of strings.

Upvotes: 0

adarshr
adarshr

Reputation: 62593

Because it's wrong. This is the way it's done correctly:

public static void main(String[] args) {
    List<Integer> iList = returList();
    for (int i = 0; i < iList.size(); i++) {
        System.out.println(iList.get(i));
    }
}

public static List<Integer> returList() {
    List<Integer> al = new ArrayList<Integer>();
    //al.add("S"); This line can't compile now!
    al.add(1);
    return al;
}

Points to note:

  1. Programing against interfaces
  2. Avoid unnecessary initializations
  3. Use type parameters on the implementation as well (RHS of assignment operator)

Upvotes: 0

Related Questions