As we know know that we can add strings and integers in list if we declare generic type for list. but the code seems not adding up the strings. what is the problem i can't understand..
class GHJ{String s;GHJ(String s){this.s=s;}}
class DFR{
public static void main(String[] g){
List<? extends Object> list=new ArrayList();
list.add((new String("ffg")));// compiler error
list.add("df");// compiler error
list.add("sdd");// compiler error
list.add(new GHJ("dff"));// compiler error
String s= list.get(0);// compiler error
System.out.println(list+" "+s);
}
}
The proper way is to use generics.
Create your list like:
List<String> list = new ArrayList<String>();
Then you don't need to cast.
Edited:
As dpb noted there're multiple types of objects. From my point of view, mixing different type of objects in a list is asking for problems. The only way of solving it is:
Don't use generics, everything will be an Object in the list
When you need to retrieve something is needed to do something like:
if (list.get(0) instanceof String) {
} else if (list.get(0) instanceof GHJ) {
} .....
In your case, there's no need for you to make your list generic. Just use
List list = new ArrayList();
This will create a list of Object types.
Just to supplement Carlos's answer:
Suggested reading from Java language guide -> http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html
and for more comprehensive coverage:
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
You have told the compiler that the type in your list is something that extends object. then you add things to the list and try and get one out. All the compiler can know that what you get out will extend object, not that it will definitely be a string. You need to tell the compiler that you are getting a string out of the list.
if you said
Object a= new String("blah");
String s;
you would need to cast a to a string to assign it to s
s= (String) a;
what happens in your example if you do
String s = list.get(3);
where you have put your new GHJ("dff")?
how is the compiler to know what type of object is in each position?
You can, but only by either using an untyped (get rid of
<? super String>
entirely, so:
List x = new List(); x.add(new Object()); x.add("blah!");
or by finding a common superclass (in this case, only Object) for the generic argument
List<Object> x = new List<Object>(); x.add(new Object()); x.add("blah!");
You can pull tricks to stuff elements into lists, bypassing generic typechecks, but this is Very Bad Juju.
list is of type List<? super String> so if you want to add something to it, then it needs to be either a String or something that's a superclass of String. The only thing that's a superclass of String is Object. GHJ is not a superclass of String so you can't add an instance of GHJ to that list.
if you add different types to the list, you have to tell the compiler which type you are getting out of the list, you can't get around that.
could you not keep separate lists for each type of object and maybe keep a map which contains lists of individual types? Then you can get each list by type and all the elements in the list will be the same type?
Yes, you can add a list and an object in the list, but the only way to do that is be to have an untyped List. In that case you loose the type information and have to manually cast the objects your are getting out of the list back to their original type.
You could use instanceof to check into which type you can cast the objects, but that's particularly ugly...
Question is : do you really need to put both Strings and Objects in the same list ? that looks like a code smell to me.
You need to cast your String from Object, like this:
String s = (String)list.get(0);
Why would it add up the string?
It doesn't compile because needs a cast :
String str = (String)list.get(0);
The wild card will mess you up here because it's not doing what you intend it to do. Wild cards are traditionally used when you need to refer to some set of generic generics.
For example, if you wanted to let the user give you a collection that contained any type of Object then your method could take a Collection<? extends Object> as its parameter. You would want to do this because if you just used Collection<Object> as the parameter then they couldn't pass a Collection<String>.
So, the problem is that List<? extends Object> has some limitations because the compiler cannot know what specific type that List takes. For example, it could be a List<String>... the compiler has no idea. For that reason, you could never call any method on List that takes a <? extends Object> as its parameter. In other words, you cannot call add().
You could call get()... because the compiler knows what type the receiving variable is and can check that it's ok. Object foo = myList.get(0) is fine.
I think in this case you really want List<Object> and to make sure to declare the type parameter on the ArrayList as well.
List<Object> list = new ArrayList<Object>();
And if you want strings then you will have to specifically convert the Objects to string. I suggest String.valueOf() personally.
Incidentally, the above is documented pretty well in the wildcards section of the Generics chapter in Core Java volume 1 (well, at least my ancient JDK 1.5 edition).
Related
I have my first list as
List<A> a
I have another list as
List<X.Y.Z> b
How do I add first list to the second one ?
I tried casting -
b.add(List<X.Y.Z>)a) - did not work
Tried adding through iteration of first list - did not work
definitely missed something ?
Unless there is an Inheritance relationship between A and X.Y.Z you cannot have them in the same container because they are not of the same type
You can use the generic superclass Object as the type of the List and this will work.
This is not possible as the reference types for both collections are different. The only way items from one List can be merged with those from another is if they both are of type List<Object> or the types themselves are identical (or at least derived from the same type).
The reason is because of type of List<>
X.Y.Z != A
You can use List<Object>, to which you can add() anything.Even though you added like that
you would have to cast each one back,while getting back.
You need to cast list a as the same type as list b so that they are the same type of object. Check out this article
It should also be noted that if you want to add the elements of List<A> a to List<X.Y.Z> b (which I assume is your intent), rather than the List<A> a itself as an element, you should use the addAll() method, not the add() method. But again, this won't work unless A is a subclass of X.Y.Z, and if A is a super class of X.Y.Z then casting the A variable will only work if it is an instance X.Y.Z.
Either you use List<Object>, to which you can add anything, or you write a method somewhere to convert an object of type A to X.Y.Z.
Notice that, if you use List<Object>, you'll need to cast the object to the desired class when you get it:
List<Object> myList = new List<Object>;
// ...
A myObject = (A) myList.get(0);
X.Y.Z otherObject = (X.Y.Z) myList.get(1);
// ...
Consider the following case
List<Integer> l1=new ArrayList<>();
List<String> l2=new ArrayList<>();
l1.add(2);
l2.addAll((List<String>)l1);
you are trying to do the same thing. here you can't cast integer list to string list.
Same way you can't cast A type list to X.Y.Z. type.
In Java, and Android, I end up using ArrayList<String> for the supplying list as I find them easier to use than the standard String[]. My real questions though are this:What is the <String> portion of the ArrayList<String> called?How can I create classes and use the <> [modifier]? (I don't know what it's actually called, so for now it's modifier).Thanks!
Here, you wil maybe see clearer:
ArrayList<TypeOfYourClass>
You can create a Person class and pass it to an ArrayList as this snippet is showing:
ArrayList<Person> listOfPersons = new ArrayList<Person>();
The <String> part is the type argument. It provides a "value" of sorts for the type parameter which is the E in ArrayList<E>... in the same way that if you have a method:
public void foo(int x)
and you call it with:
foo(5)
the parameter is x, and the argument supplied is 5. Type parameters and type arguments are the generic equivalent, basically.
See section 4.5 of the JLS (and the links from it) for some more details - and the Java Generics FAQ for more information about generics than you could possibly want to read :)
Supose you want an ArrayList to be filled only with Strings.
If you write:
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
You can be sure than if somebody tries to fill the arrayList with an int it will be detected in complile time.
So, generics are used in situations where you want enforce restrictions like this.
The bit between <> is a type argument, and the feature is called Generics
Look up the generics syntax for Java. That will set you straight (well, sort of; a lot of people find Java's approach inferior to C++ and C#).
The is the type parameter. In Java, you have to provide one of these when the class is written as a generic.
Here is an example of a Generic class definition
private class GNode<T>
{
private T data;
private GNode<T> next;
public GNode(T data)
{
this.data = data;
this.next = null;
}
}
You can now create nodes of any type that you pass in. The T acts as a generic type parameter for your class definition. If you want to create a node of Integers, just do:
GNode<Integer> myNode = new GNode<Integer>();
It should be noted that your type parameter must be an object. This works through Java's auto-boxing and auto-unboxing. This means that you cannot use java primitive types and you must use the corresponding classes instead.
Boolean instead of bool
Integer instead of int
Double instead of double
etc...
Also, if you don't pass in a type parameter I'm pretty sure your code will still compile. But it won't work.
Is this possible without going through the list and casting the objects?
I also need to convert List<Object> to List<T> (T = predefined Object) if it's possible?
Edit: for clarification, I'm trying to use List<Object> as a return type of a class method that is widely used in my code.
Actually, this is possible, because of type erasure. You can convert a parameterized type to a raw type, and vice-versa.
List<Object> listO = new ArrayList<Object>( );
listO.add( "Foo" );
listO.add( "Bar" );
List listQ = listO;
List<String> listS = (List<String>) listQ;
However, this does not mean this is a good idea. This works around compile-time type-checking of parameterized types. If your List contains objects other than the type you expect, unexpected results may occur.
No. This is simply not a valid conversion, because not all Objects are String[].
You could have determined this for yourself in 2 lines of code.
Edit
It sounds like you need to write the method more generically. Something like this:
public <T> List<T> getGenericList()
{
return new ArrayList<T>();
}
This can return a List<String[]> like so:
List<String[]> listOfStringArr = getGenericList();
It is not possible by definition. All classes in java extend Object. List of Objects can theoretically contain elements of every type your want. How do you want to convert something to specific type T?
No. What if the Object in the first list isn't actually T?
That being said, you can try to cast List<Object> to List<T> (be prepared for exceptions).
/e1
I don't know the specifics of your code, but it sounds like creating a generic method would be beneficial to you.
I am trying to use a common technique to create objects from Xml. (Xml is legacy, so although there are already libraries to do this, it seemed faster to write this myself.)
I don't understand the compiler's complaint about the generic usage. Code sample:
public void createObjects() {
List<Object1> objectOnes = new ArrayList<Object1>();
List<Object2> objectTwos = new ArrayList<Object2>();
parseObjectsToList("XmlElement1", objectOnes);
parseObjectsToList("XmlElement2", objectTwos);
}
private void parseObjectsToList(String xmlTag, List<? extends Object> targetList) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(xmlTag);
targetList.add(newObj)
/* compiler complains: "The method add(capture#2-of ? extends Object) in the type List<capture#2-of ? extends Object> is not applicable for the arguments (Object)"
*/
/* If I change method signature to parseObjectsToList(String xmlTag, List targetList)
it works fine, but generates compiler warning about raw type */
}
Thanks for any enlightenment on the subject!
The problem you are running into is that, with the bounded wildcard that you have defined, you will be unable to add any element to the collection. From this tutorial:
List<? extends Shape > is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.
There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method
All a wildcard type means is that the actual type parameter T of the List that you pass as the second argument to parseObjectsToList is going to be a subtype of Object. It does NOT mean that the same List will be parameterized with different types.
So now you have a List<T> (called targetList) and you are trying to call targetList.add(Object). This is illegal because Object is not necessarily a subtype of T.
Because you are adding to the List rather than extracting elements from it, use List<Object> and make sure that's exactly what you pass in.
Using a List<Object> will work, but you might want keep your more precisely typed List<Object1> and List<Object2> for type-safety elsewhere. In that case, you'll need to check the type of each object before adding it to the List.
private void parseObjectsToList(String tag, List<T> list, Class<? extends T> c) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(tag);
list.add(c.cast(newObj)) ;
}
The cast() operation is a reflective equivalent to the static cast operator: (T) newObj
Using the altered method would look something like this:
parseObjectsToList("XmlElement1", objectOnes, Object1.class);
Think about what you are asking the compiler to do:
Given a list of "something that is a subtype of Object
Let me insert an Object into it
This doesn't make sense. Suppose your list is a list of Integer. Suppose that createObjectFromXml returns a String. It wouldn't make sense to allow inserting a String into a list typed for Integers.
So, your options are either to make your List a List<Object> or to find some way to make createObjectFromXml return a specific type, that you can then tie to the type of your list.
public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
The call to the method will not type-check. I can't even cast the parameter as follows:
wahey((List<Object>) new LinkedList<Number>());
From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:
List<Double> ld;
wahey(ld);
Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). Now, after the method call, ld refers to a list with a type List<Double>, but the actual list contains some String objects!
This seems different to the normal way Java works without generics. For instance:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.
This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?
What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
This looks perfectly fine and type-safe until you call it like this:
List<Double> list_d;
String s;
Foo(list_d, s);
Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.
So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.
Consider if it was...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.
They aren't subtypes of each other due how generics work. What you want is to declare your function like this:
public void wahey(List<?> list) {}
Then it will accept a List of anything that extends Object. You can also do:
public void wahey(List<? extends Number> list) {}
This will let you take in Lists of something that's a subclass of Number.
I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.
There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object.
This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). And it doesn't, and the compiler can detect it, so it doesn't let you get away with it.
"What we are doing here is essentially
the same thing, except this will pass
compile-time checks and only fail at
run-time. The version with Lists won't
compile."
What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time.
From java.sun.com
Generics provides a way for you to
communicate the type of a collection
to the compiler, so that it can be
checked. Once the compiler knows the
element type of the collection, the
compiler can check that you have used
the collection consistently and can
insert the correct casts on values
being taken out of the collection.
In Java, List<S> is not a subtype of List<T> when S is a subtype of T. This rule provides type safety.
Let's say we allow a List<String> to be a subtype of List<Object>. Consider the following example:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
Here you have a collection of T's, you want to add all items from another collection. You can't call this method with a Collection<S> for an S subtype of T. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.
To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:
class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.