Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ArrayList<Integer> takes String

public class Main {
    public static void main(String[] args) {

        ArrayList<Integer> ar = new ArrayList<Integer>();
        List l = new ArrayList();
        l.add("a");
        l.add("b");
        ar.addAll(l);
        System.out.println(ar);
    }
}

Output: [a,b]

You can't directly add String to ArrayList<Integer> ar, but by using addAll() it is possible.

How can we add String to ArrayList whose type has already been specified as Integer? Can anyone highlight clear implementation details and the reason behind this?

like image 719
Santanu Avatar asked Mar 24 '15 12:03

Santanu


People also ask

Can we store String and integer together in an ArrayList?

Yeah, you can create an ArrayList<Object> but having said that, my advice for you is this: don't. Don't create Lists with mixed types since this suggests that your program design is broken and needs to be improved so that this sort of monster isn't needed.

Can ArrayList hold String?

The Java collection classes, including ArrayList, have one major constraint: they can only store pointers to objects, not primitives. So an ArrayList can store pointers to String objects or Color objects, but an ArrayList cannot store a collection of primitives like int or double.

Does ArrayList have to String method?

Note: The ArrayList class does not have its own toString() method. Rather it overrides the method from the Object class.

Can an ArrayList be integers?

For int data type, the wrapper class is called Integer . So, to create an ArrayList of ints, we need to use the Integer wrapper class as its type. We can now add integers to the ArrayList by using the class's add() method. ArrayList , just like normal arrays, follow zero-based indexing.


4 Answers

But how can we add strings to arraylist whose type has already been specified as Integer?

Because of the way Java generics was designed for backwards compatibility, with type erasure and raw types, basically.

At execution time, there's no such things as an ArrayList<Integer> - there's just an ArrayList. You're using the raw type List, so the compiler isn't doing any of its normal checks, either at compile-time or adding execution-time casts.

The compiler does warn you that you're doing unsafe things though:

Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

... and when you recompile with the relevant flag, it will warn about everything, including probably the most surprising line:

ar.addAll(l);

That's the one that surprises me somewhat, in terms of compiling - I believe it's effectively trusting that the List is a Collection<? extends Integer> really, when we know it's not.

If you avoid using raw types, this sort of mess goes away.

like image 164
Jon Skeet Avatar answered Oct 27 '22 07:10

Jon Skeet


This is more about mixing of raw and generic types in Java's type system than it is about type erasure. Let me augment the code fragment from the question:

    ArrayList<Integer> ar = new ArrayList<Integer>();
    List l = new ArrayList();      // (1)
    l.add("a");
    l.add("b");
    ar.addAll(l);                  // (2)
    System.out.println(ar);
    Integer i = ar.get(0);         // (3)

With today's erased generics, line (3) throws ClassCastException. If Java's generics were reified, it is easy to assume that runtime type checking would cause an exception to be thrown at line (2). That would be one possible design of reified generics, but other designs might not do that checking. Why not? Mainly for the same reason we have erased generics today: migration compatibility.

Neal Gafter observed in his article Reified Generics for Java that there are a lot of unsafe uses of generics, with improper casts, and so forth. Today, even more than ten years after generics were introduced, I still see a lot of usage of raw types. (Including, unfortunately, here on Stack Overflow.) Unconditionally performing reified generic type checking would break a huge amount of code, which of course would be a big blow to compatibility.

Any realistic generic reification proposal would have to provide reification on an opt-in basis, such as via subtyping (as in Gafter's proposal), or via annotations (Gerakios, Biboudis, Smaragdakis. Reified Type Parameters Using Java Annotations. [PDF] GPSE 2013.), and it would have to decide how to deal with raw types. It seems wholly impractical to disallow raw types entirely. In turn, allowing raw types effectively means that there is a way to circumvent the generic type system.

(This sort of decision is not undertaken lightly. I've witnessed shouting matches between type theorists, one of whom was complaining that Java's type system is unsound. For a type theorist, this is the most grievous of insults.)

Essentially, that's what this code does: it bypasses the generic type checking goodness by using raw types. Even if Java's generics were reified, checking might not be done at line (2). Under some of the reified generics designs, the code might behave exactly the same as it does today: throwing an exception at line (3).

In Jon Skeet's answer, he admits to being somewhat surprised that at line (2) the compiler trusts that list l contains elements of the right type. It's not really about trust -- after all, the compiler does issue a warning here. It's more the compiler saying, "Ok, you're using raw types, you're on your own. If you get a ClassCastException later, it's not my fault." Again, though, this is about allowing raw types for compatibility purposes, not erasure.

like image 45
Stuart Marks Avatar answered Oct 27 '22 05:10

Stuart Marks


You are using a raw type. If you use List<String> l = new ArrayList<>() you will find that your code will not compile anymore. Raw types exist only for backwards compatibility and should not be used in new code.

like image 7
Hoopje Avatar answered Oct 27 '22 05:10

Hoopje


When it was born, Java did not have generics (that is, classes that are parameterized by another class). When generics were added, to maintain compatibility, it was decided not to change the Java bytecode and class file format. So, generic classes are transformed by the compiler into non-generics ones. This means that an ArrayList is actually storing instances of class Object, and so it can also accept instances of String (that is a subclass of Object). The compiler cannot always detect misuses.

like image 6
lodo Avatar answered Oct 27 '22 07:10

lodo