better solution with cast in generic java - java

Suppose I have the following code:
Object li = new Object();
// this causes a compiler warning
ArrayList<String> lo = (ArrayList<String>)(li);
Okay, I have edited the question, sorry, I made a mistake, the point is, since it will cause a compile time warning when we cast to the target, whose target is a parameterized type, is there any better solution so that I can prevent the code from causing a compile time warning?
When dealing with arrays, we have Arrays.newInstance to replace the use of cast.
code from original question:
// List<String> is a superclass of ArrayList
List<String> listOne = new List<String>();
ArrayList<String> listTwo = (List<String>) listOne;

You can't instantiate a List, because List is an interface.
An ArrayList is a class that implements the List interface.
You can do this:
List<String> listOne = new ArrayList<String>();
List<String> listTwo = listOne;
This is the correct approach when the code using listOne doesn't care what kind of List it is.
Here's an example that demonstrates why this is a good approach:
List<String> someList = createList();
Here someList is set to the return value of a method named createList. In this situation we have no idea what kind of list someList is. It could be an ArrayList, a Vector, a Stack, etc... As long as createList returns an object that implements the List interface, the above code will work. With this approach, the code in createList can be modified without affecting the code that calls it.
You can also do this:
ArrayList<String> listOne = new ArrayList<String>();
ArrayList<String> listTwo = listOne;
This is not as flexible, but it allows you to treat your lists specifically as ArrayLists.
Technically, you can do this, but it's not a good idea:
List<String> listOne = new ArrayList<String>();
ArrayList<String> listTwo = (ArrayList<String>) listOne;
It's better to program to an interface by declaring a List and instantiating an ArrayList.
Here's an example that shows some benefits of programming to interfaces:
public static void testIt() {
List someList;
ArrayList anArrayList;
/*
* all of these will work
*/
someList = createVectorAsList();
printList(someList);
someList = createStackAsList();
printList(someList);
someList = createArrayListAsList();
printList(someList);
// you CAN assign an ArrayList to a List
someList = createArrayList();
printList(someList);
// you CAN treat an ArrayList as a List
anArrayList = createArrayList();
printList(anArrayList);
/*
* none of these work
*/
// you can NOT assign List to an ArrayList
anArrayList = createStackAsList();
anArrayList = createVectorAsList();
// you can NOT pass anything but an ArrayList to printArrayList
printArrayList(createStackAsList());
printArrayList(createVectorAsList());
printArrayList(createArrayListAsList());
}
/** Prints any List */
public void printList(List someList) {
for (Object o : someList) {
System.out.println(o.toString());
}
}
/** Prints ArrayLists only */
public void printArrayList(ArrayList someList) {
for (Object o : someList) {
System.out.println(o.toString());
}
}
public List createVectorAsList() {
Vector v = new Vector();
v.add("I am a vector element");
return v;
}
public List createStackAsList() {
Stack s = new Stack();
s.add("I am a stack element");
return s;
}
public List createArrayListAsList() {
ArrayList ar = new ArrayList();
ar.add("I am an array list element");
return ar;
}
public ArrayList createArrayList() {
ArrayList ar = new ArrayList();
ar.add("My array is not returned as a list...");
return ar;
}

If you are in such a situation, you have two options. The first is to do an unchecked cast:
Object o = getObjectFromSomewhereMysterious();
List<String> lst = (List<String>)o; //compiler warning
This will cause the warning you mention - what it means is that, because of type erasure,
at runtime the cast can only check to make sure o is a List, but not a List<String>. So for example if o is a HashMap the cast will fail immediately, but if it's a List<Integer> it won't fail... until some later time when you try to treat an element of lst like a String. This is known as "polluting the heap":
//some later place in the code path - who knows, it could be miles away
String str = lst.get(0); //ClassCastException if lst isn't really a List<String>
As long as you can be sure that the cast is safe, you can suppress the compiler warning:
#SuppressWarnings("unchecked") //this is safe because blah blah blah
List<String> lst = (List<String>)o;
Always document why the warning can be suppressed - this helps to keep the code maintainable.
The second option is to play it safe:
List<?> lst = (List<?>)o;
This means lst is a List of some unknown type. It allows you to avoid the unchecked cast, but places restrictions on what you can do with lst:
lst.add("some string"); //compiler error
That statement is illegal since we don't know whether lst is allowed to hold Strings. The best we can do is read from it, but even then elements are only typed as Object:
Object element = lst.get(0);

Related

How casting of generics works Java

If I declare a list "myList" that I happen to know will only contain strings I can cast it to a List without any problems, what is actually going on behind the scenes to cast the list to a list of strings?
List myList = new ArrayList();
List<String> myStringList = (List<String>) myList;
If I create the list as list of objects I would assume that the same thing applies but I can no longer cast this back to a list of strings, what is the reason for this?
List<Object> myObjList = new ArrayList<Object>();
List<String> myStringList = (List<String>) myObjList; // Can't do this!
List myList = new ArrayList();
List<String> myStringList = (List<String>) myList;
Works for compatibility reasons with pre-generics code. It works, but it can give you errors at runtime if the raw List contains not only Strings.
Generics are supposed to protect you at compiler time. They don't let you cast a list of Objects into a list of String, because the List<Objects> could contain objects that are not String and it would result in errors at runtime.
Strings are Objects but Objects are not Strings, that is why you can't do the second code.
I like to think of it like this: A dog is an animal and can behave like an animal but an animal is not a dog.
Good info here.
Technically adding a clause to answer by #FunctionR
Strings are Objects but Objects are not Strings
But Objects can be String
Object is a base class of String. So you can do cast on both direction.
For example,
Object a = null;
String s = "";
a = s;
s = (String)a;
So String can be a Object.
But in List<Object> and List<String>
You should do this
List<Object> myObjList = new ArrayList<Object>();
List<String> myStringList = (List) myObjList;

What is the best way to convert a raw vector to type safe list(Arraylist)

I have a function that return a raw vector. I know that all the elements in the vector are string but the code stays for leagacy reasons. I want to get a arraylist from this data.
One naive way is to iterate the vector and add elements to the list. Is there any short way of doing it which can prevent looping. Or may be a direct function which enables this.
Edit:
Example:
Vector f1() {} //f1 returns raw vector
I want to achieve the following:
List<String> l = new ArrayList<String>();
Vector vec = f1();
for(Object obj: vec) {
l.add((String) obj);
}
Note: I have not checked if the above code compiles. Please treat it as a pseudo code
If you are 100% sure the Vector only contains Strings, the simplest way is:
List<String> list = new ArrayList<>(vector);
Note that this will compile and run fine, even if you Vector contains other types of objects. However this:
list.get(i);
will throw a ClassCastException if the i-th element was not a String.
Since you have a raw Vector you will get warnings. If you want to get rid of them you can use:
#SuppressWarnings(value = {"unchecked", "rawtypes"})
public static List<String> rawVectorToList(Vector v) {
return new ArrayList<>(v);
}
An alternative to detect casting issues fast is to copy the array manually (what the copy constructor does under the hood):
Vector v = ...;
String[] elements = Arrays.copyOf(v.toArray(), v.size(), String[].class);
List<String> list = Arrays.asList(elements);
or if you need the list to be mutable:
List<String> list = new ArrayList<> (Arrays.asList(elements));
This has the benefit of checking the type at copy time.

generics with different approach

In an interview with regard to generics the interviewer ask a question that there are two list,one list is of String type (that is one arraylist is of string type) and we add some strings in it, now create the second array list which will be of type the the first list you created , Now the possible solution was as shown below..
List<String> firstList = new ArrayList<String>();
public <T> List<T> createSecondList(List<T> firstList)
{
return new ArrayList<T>();
}
my query was can we express this in the below way also please advise.
List<String> firstList = new ArrayList<String>();
<T> List<T> createSecondList = new ArrayList(List<T> firstList);
No.
This line:
<T> List<T> createSecondList = new ArrayList(List<T> firstList);
Is not valid Java code, its kind of half way between a method declaration and a variable declaration and instantiation..
To create the second list...
List<String> firstList = new ArrayList<String>();
List<String> secondList = createSecondList(firstList);
1> List<String> list1 = new ArrayList<String>(); //this is first type
2> List<List<String>> list2 = new ArrayList<List<String>>(); //this is second type which is including first list type

Why can't you have a "List<List<String>>" in Java? [duplicate]

This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 5 years ago.
In Java, why doesn't the following line of code work?
List<List<String>> myList = new ArrayList<ArrayList<String>>();
It works if I change it to
List<ArrayList<String>> myList = new ArrayList<ArrayList<String>>();
At first, I thought maybe you can't have lists of an interface, but I can create a List<Runnable> just fine.
Ideas?
Generic types are more pedantic.
List means List or any sub-type, but <List> means only List. If you want a sub-type you need to have <? extends List>
I suspect you can use
List<List<String>> myList = new ArrayList<List<String>>();
The reason you can't do this is that you can be using a reference to a reference and with an extra level of indirection you have to be careful.
// with one level of indirection its simple.
ArrayList alist = new ArrayList();
List list = aList; // all good
list = new LinkedList(); // alist is still good.
With generics you can have two level of indirection which can give you problems so they are more pedantic to avoid these issues.
// with two levels of indirection
List<ArrayList> alist = new ArrayList<ArrayList>();
List<List> list = (List) alist; // gives you a warning.
list.add(new LinkedList()); // adding a LinkedList into a list of ArrayList!!
System.out.println(alist.get(0)); // runtime error
prints
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
cannot be cast to java.util.ArrayList
Lets start with this:
ArrayList<ArrayList<String>> myList = new ArrayList<ArrayList<String>>();
This is creating an ArrayList whose elements are ArrayLists.
Now suppose we could assign that to
List<List<String>> myList2 = myList.
Now, we should be able to do this:
myList2.add(new LinkedList<String>());
But that means we have added a LinkedList to a list whose elements are supposed to be ArrayLists. Ooops!!!
In reality, the assignment of myList to myList2 is not legal ... and that ensures that it is not possible to add the wrong kind of List<String> to the ArrayList<ArrayList<String>> object. (No Peter, it is not just pedantry :-) )
Only the top level collection can be declared as an implementing class, while the nested ones must remain interfaces until you actually create instances:
List<List<String>> rootList = new ArrayList<List<String>>();
and then when you create an element to go in, you make it an implementation:
List<String> nodeList = new ArrayList<String>();
rootList.add(nodeList);
Its comparing Type from left(declaration) side to Type from right(instantiation) side. In Left, your type is List<String> while in right, it's ArrayList<String>. If complaining about the difference.
Please update the right side(instatiation) as List i.e.
List<List<String>> myList = new ArrayList<List<String>>();
This should work fine.
I know this is an old question but I just wanted to share my idea.
Instead of making a List of Lists, I personally just make a List of Type[] (List<Type[]> listArray = new ArrayList<Type[]>();), I generate a separate List of just Type (List<Type> list = new ArrayList<Type>();), then .add(list.toArray()). This way, it's clearer and easier to read than the List of Lists syntax which is confusing.
For example, in a recent project where I had an input file where each line with only a "0" meant a new line in the original (it was an encryption algorithm):
String[] input = getInputContents(inFile);
List<String> currentBuffer = new ArrayList<String>();
List<String[]> buffers = new ArrayList<String[]>();
for(String line : input) {
if(line.equals("0")) {
buffers.add((String[])currentBuffer.toArray());
currentBuffer = new ArrayList<String>();
} else {
currentBuffer.add(line);
}
}
the list<list<string>> l1=new list<list<string>>(); is allowed if the list contains one more list inside the list.
public final class CPanelXMLBuilder extends PanelXMLBuilder {
public CPanelXMLBuilder(AuthenticatedUser pAuthenticatedUser, Map<String, Object> pSessionMap, Map<String, Object> pRequestMap, String pPanelTemplate) throws Exception {
super(pAuthenticatedUser, pSessionMap, pRequestMap, pPanelTemplate, null);
}
public Map<String, Object> buildXMLDocument(List<List<String>> pDetailsList) {
if (pDetailsList.size() == 1) {
List<String> pCustomerDetail = pDetailsList.get(0);
xmlDocument.getRootElement().getChild("E_SHOW1").setText(pCustomerDetail.get(0));
xmlDocument.getRootElement().getChild("E_SHOW2").setText(pCustomerDetail.get(1));
xmlDocument.getRootElement().getChild("E_SHOW3").setText(pCustomerDetail.get(2));
xmlDocument.getRootElement().getChild("E_SHOW4").setText(pCustomerDetail.get(3));
xmlDocument.getRootElement().getChild("E_SHOW5").setText(pCustomerDetail.get(4));
xmlDocument.getRootElement().getChild("ServerTimestamp").setText(pCustomerDetail.get(5).substring(0, 19));
} else {
xmlDocument.getRootElement().getChild("AlertType").setText("INFO");
xmlDocument.getRootElement().getChild("Alert").setText("There is no matching record.");
}
requestMap.put(RequestMapKeys.XML_DOCUMENT, xmlDocument);
return requestMap;
}
}

Java 1.6: Creating an array of List<T>

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]);
}

Categories