List<String> v = new ArrayList<String>();
I understand the generics helps you declare that the ArrayList() has Objects of type String. My question is
how is the following one different from the above?
List<String> v = new ArrayList();
or the one below different from others
List v = new ArrayList<String>();
List<String> v = new ArrayList();
This one isn't really functionally different. The type parameter on the right side doesn't really do anything. It's used as a matter of style and to avoid the use of a Raw type, which is considered a programming error. In fact, in Java 7 it has been enhanced so you can just do this: List<String> v = new ArrayList<>(); and not have to repeat yourself on the right hand side.
List v = new ArrayList<String>();
The list with no type parameter is called a Raw Type. It is generally considered a programming error to declare a Raw Type in new code that is using generics. Basically there is no type checking going on at all when you declare it this way, you can put anything in that list.
Java generics are a compile time check. So it is the type of the reference at compile time that matters. If your reference is of type Raw List it doesn't matter what you declared on the right hand side, that is what the compiler will check against.
List<String> isn't really a "List that has Strings." It is a "List that I have asked the compiler to return errors and/or warn me if I put something in there that isn't a String. If you ignore compiler warnings, it is perfectly possible to get stuff in there that isn't a String.
List v = new ArrayList();
It is before java 5 way of declaring a list.
List<String> v = new ArrayList<String>();
It uses generics, introduced in java 5. It adds compile-time type safety.
List<String> v = new ArrayList<>();
is just an optimization introduced in java 7. It just simplifies the code
by maintaining the type safety
public static void main(String[] args) throws Exception {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList(); //This is equivalent to list1 but with compilation warning
List list3 = new ArrayList<Integer>(); //This is equivalent to list3 = new ArrayList<Object>()
//list1.add(new Object()); //does not compile
//list2.add(new Object()); //does not compile
list3.add(new Object()); //this is fine
list1 = list3; //ok, but
System.out.println(list3.get(0)); // this is fine
System.out.println(list1.get(0)); //Runtime error: ClassCastException
//List<Object> list5 = list1; //does not compile
List<Object> list5 = list3; //ok
}
These all compile, and are all valid. The second case is likely only going to throw a warning - you won't be able to add anything that isn't a string due to List<String>. In the third case you have the opposite problem. You CAN add things that aren't Strings, but may run into a runtime exception as a result.
In the first and third case (new ArrayList<String>()) you instantiate an ArrayList that can hold String instances
In the second case (new ArrayList()) you instantiate an ArrayList that can hold Object instances (that is instances of any type -- even mix & match)
In the first and second case (ArrayList<String> v) you declare an instance of ArrayList that can hold String instances
In the third case (ArrayList v) you declare an instance of ArrayList that can hold instances of Object.
The problem with the second case is that if you were to get hold of a "raw" ArrayList (like the one instantiated), then it could hold anything theoretically; not just String instances, which is what the users of the declaration expect.
Similarly, in the third case, you create an ArrayList that is supposed to hold Strings, but the users of the declaration do not know that and might try to put other object instances in it
Note: of course under the hood, in the JVM, the generic type information is lost, so there are no differences in terms of execution, but for programming type safety, the compiler will flag the incorrect use. This way, there is no need to dynamically check/ensure the type of the objects put into/coming out of the list -- you can assume they are the correct type, because the compiler ensured this
Related
I declared and initialized myList with a raw reference type of List and a raw object type of ArrayList. Then, I re-referenced myList to a new, generic ArrayList of Longs. I thought that adding anything other than a Long to this list would thus cause an error.
List myList = new ArrayList();
myList = new ArrayList<Long>();
myList.add(3.4d);
myList.add(4.0f);
myList.add("weird");
myList.add('w');
System.out.println(myList);
However, this runs without an error or exception. How is this legal?
If you declare it as List<Long> you will get static compile time type checking. Do to type erasure the JVM does not know anything about those types at runtime.
List<Long> myList = new ArrayList<>();
myList.add("foo");
Will give a compilation error while:
public void breakGeneric(List list) {
list.add("foo");
}
....
List<Long> myList = new ArrayList<>();
breakGeneric(myList);
will add "foo" to a list no matter what type type is. Most IDEs will worn you about loosing the generic type.
Having the type in the new statement new ArrayList<Long>() would only have an effect if you chain off of that statement ie new ArrayList<Long>().add("foo"). That is the only way that a generic type only in the new statement will cause a compilation problem.
How is this legal?
Because the java compiler only considers the declared type, which in this case is the raw type List:
List myList
that can hold any type of object.
The assignment myList = new ArrayList<Long>() has no effect on the declared type of myList.
This question already has an answer here:
Difference between LinkedList<?> and LinkedList<Object> [duplicate]
(1 answer)
Closed 4 years ago.
(Note: this is not the same as this question about vs. !)
I'm confused by Java's generics.
ArrayList<? extends Object> x = new ArrayList<String>();
ArrayList<Object> y = new ArrayList<String>();
In this example, the first line compiles while the second one doesn't. Why is that?
I thought an assignment of the shape List<A> x = new ArrayList<B>() should be valid as long as B extends A, i.e. the right side has higher specificity than the left side of the assignment, but apparently I'm mistaken.
Could somebody elaborate on the similarities and differences of these statements?
In Java, the type arguments must match exactly.
Consider the following code snippet:
ArrayList<Object> list = new ArrayList<String>();
list.add(new SomeClass());
We have now succeeded in adding a non-string to ArrayList<String>. There is nothing wrong with the second statement: we're adding some object to an ArrayList<Object>. So there must be something wrong with the first statement! That's why Java does not let you do that.
If you change the snippet to:
ArrayList<? extends Object> list = new ArrayList<String>();
list.add(new SomeClass());
you will notice that the error is now in the second line. You cannot add an object to the list.
The difference between the two is the following:
ArrayList<Object> means "an ArrayList containing Objects". Such a list can of course also contain instances of subclasses of Object. Since every object is an instance of Object, this list can contain any object (in this case).
ArrayList<? extends Object> means "an ArrayList containing instances of some unknown subclass of Object". Such an ArrayList cannot contain every object. Only instances of this unknown subclass can be added to it. Since the specific subclass is not known, it is not possible to add elements to it here. (But elsewhere, the same list may be known as an ArrayList<String>, and there it is of course possible to add elements to it.)
Both below cases won't work because you are initializing different object type than expected:
ArrayList<Object> y = new ArrayList<String>();
ArrayList<String> y2 = new ArrayList<Object>();
If you use ? wildcard you get some more options to have different, but related by inheritance types (String extends Object implicitly):
ArrayList<? extends Object> x = new ArrayList<String>();
Consider also using the diamond operator
ArrayList<Object> y = new ArrayList<>();
You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.
String abc = "abc";
Object x = abc;
works but
List<Object> list = new ArrayList<String>();
does not because Generic expressions have to match exactly if they do not contain a wildcard.
Please note that on the declaration side it is better to use List (and not ArrayList) because it allows you to change the implementation at a later stage, e.g. to LinkedList.
If we have a Type[], we can only store Type or its subtypes in it. The same goes for ArrayList. So why is it said that one is homogeneous while the other is not?
Arrays have a runtime check on the type of the added element. That is, if a new element that is not of the same type is added, an ArrayStoreException is thrown at runtime. That's why they are considered as "homegeneous".
This is not true for ArrayLists (Lists in general). Due to type erasure at runtime, it can practically hold any object.
The following throws an exception when running:
Object[] array = new String[3];
array[0] = "a";
array[1] = 1; // throws java.lang.ArrayStoreException
unlike the following which compiles and runs without problem (although with a compiler warning as it doesn't properly use generics):
ArrayList list = new ArrayList<String>();
list.add("a");
list.add(1); // OK
list.add(new Object()); // OK
With a correct use of generics, i.e. declaring the variable list above of type ArrayList<String> instead of ArrayList, the problem is avoided at compile-time:
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add(1); // compilation error
list.add(new Object()); // compilation error
But even with a generically declared list, you can have something like this work without an exception at runtime:
ArrayList<String> list = new ArrayList<String>();
list.add("a");
Method[] methods = List.class.getMethods();
for(Method m : methods) {
if(m.getName().equals("add")) {
m.invoke(list, 1);
break;
}
}
System.out.println(list.get(0));
System.out.println((Object) list.get(1));
Output:
a
1
Yes.Java Arrays are homogeneous,because when you declare any array in Java you have to declare its type.
eg:
int arr[]; //type is int
String arr[]; //type is String
float arr[]; //type is float
now if you try to store any other data-type in declared array,it will be a compile time error.
eg:
int arr=new int[5];
arr[0]="I am a String not int"; //compile time error
but ArrayList are the Collection's part,they hold Objects,instead of any specific data-type[if we are not talking about generics],and because every thing in java is directly or indirectly inherited from Object class,so It will not give you compile-time error,type checking will be on run-time.
eg:
ArrayList al=new ArrayList();//type is Object
al.add("I am a String"); //Bacause String class in inherited from Object Class
al.add(1);//the int 1 will first autobox into Integer class then stored in al ArrayList.Now bacause Integer class is also inherited from Object class,it will*/ allow you to store
al.add(UserDefinedClass); //because every User defined class is also inherited from Object class,so it will also allow you.
Now did you notice,because we have not defined any data type of ArrayList al,but still we are storing different type values: this is know why ArrayList Stores Object not specific data-type,thus they are heterogeneous not homogeneous.
i have a question about creating a new array list.
If i create a new one, with
ArrayList <?> listtwo = new ArrayList<?>();
What can i put into the array? Can i put Strings and Integers in it?
Or how does this work.
I know that if you create an list like :
ArrayList<String> list =new ArrayList<String>();
that you can only put Strings in it. and if u try to do something else it gives a compile exception.
If i create a new one, with
ArrayList <?> listtwo = new ArrayList<?>();
You can't. You can't instantiate a parameterized type without bounds. Here's what the compiler will say:
ArrayList <?> listtwo = new ArrayList<?>();
^
required: class or interface without bounds
found: ?
So the question of what you can put in it is moot.
If you didn't supply a type parameter at all:
ArrayList listtwo = new ArrayList();
...you could put anything you liked into it (including a mix of things). Primitive values will get coerced to their object equivalents (int => Integer, etc.). At that point, it's basically an ArrayList<Object> instance.
There are two issues here.
(a) new ArrayList<?> is not a legal expression.
If you want to create a list that can hold String and Integers you need to use ArrayList<Object> as Object is the (most specific) common super-type of String and Integer:
ArrayList<Object> list = new ArrayList<Object>;
list.add('abcd'); // insert a string
list.add(1234); // insert an integer
The down side is that when you extract an item from this list it will be of type Object so you will need to downcast it:
String s = (String) list.get(0);
Integer n = (Integet) list.get(1);
(b) Regarding the wildcard ? symbol:
It can be used for specifying the type of a variable, as in: ArrayList<?> list = ... but it cannot be used in a new expression. In other words, this expression is illegal: new ArrayList<?>.
The wildcard is useful for allowing (limited) polymorphism between lists. Specifically, if you have a List of Integers and you want to treat it like a list of Number (which makes sense as Integer is a subclass of Number) you can use the wildcard symbol.
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(100);
list2.add(200);
// This assignment is OK:
ArrayList<? extends Number> list3 = list2;
Number n = list3.get(0); // n is now 100
However, the problem with list3 (or any other list with a wildcard type) is that you cannot mutate it. For instance, if you try to add an element to this list:
list3.add(300); // <-- This is not allowed
you will get a compile-time error.
Why can't I create an array of List ?
List<String>[] nav = new List<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of List"
or
ArrayList<String>[] nav = new ArrayList<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of ArrayList"
or
List<String>[] getListsOfStrings() {
List<String> groupA = new ArrayList<String>();
List<String> groupB = new ArrayList<String>();
return new List<String>[] { groupA, groupB };
}
But I can do this:
List[] getLists() {
return new List[] { new ArrayList(), new ArrayList() };
}
Eclipse says that List and ArrayList are raw types but it compiles...
Seems pretty simple, why won't it work?
Well, generics tutorial give the answer to your question.
The component type of an array object
may not be a type variable or a
parameterized type, unless it is an
(unbounded) wildcard type.You can
declare array types whose element type
is a type variable or a parameterized
type, but not array objects.
This is
annoying, to be sure. This restriction
is necessary to avoid situations like:
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;
// Run-time error: ClassCastException.
String s = lsa[1].get(0);
If arrays of parameterized type were
allowed, the previous example would
compile without any unchecked
warnings, and yet fail at run-time.
We've had type-safety as a primary
design goal of generics.
You can't create arrays of generic types, generally.
The reason is that the JVM has no way to check that only the right objects are put into it (with ArrayStoreExceptions), since the difference between List<String> and List<Integer> are nonexistent at runtime.
Of course, you can trick the compiler by using the raw type List or the unbound wildcard type List<?>, and then cast it (with a unchecked cast) to List<String>. But then it is your responsibility to put only List<String> in it and no other lists.
No exact answer, but a tip:
Last example has a raw type warning because you omitted the typization of the list; it is generally a better (type safe) approach to specify which object types are contained in the list, which you already did in the previous examples (List<String> instead of List).
Using arrays is not best practice, since their use contains errors most times; Using Collection classes (List, Set, Map,...) enables use of typization and of convenient methods for handling their content; just take a look at the static methods of the Collections class.
Thus, just use the example of the previous answer.
Another solution is to extend LinkedList<String> (or ArrayList<String>, etc.), then create an array of the subclass.
private static class StringList extends LinkedList<String> {}
public static void main(String[] args)
{
StringList[] strings = new StringList[2];
strings[0] = new StringList();
strings[1] = new StringList();
strings[0].add("Test 1");
strings[0].add("Test 2");
strings[1].add("Test 3");
strings[1].add("Test 4");
System.out.println(strings[0]);
System.out.println(strings[1]);
}