Defenition of type parameter - java

I dont understand a strong defenition of type parameter in generic class or method. It's a reference type such that... what? Does it just non reifiable type? Is it true that all reference type for compiler is reifiable or not reifiable?
2.Consider the well known code:
List<? extends String> strs= new ArrayList<String>();
? extends String str;//error
strs.add("sd");//error
Why ? extends String str; does not valid? I thinked that if we declare a reference to a generic type, for example List<E> strs; for some type E then compiler define type parameter to a specific type E.
I dont understand what occur in the case List<? extends String> strs? Does List<? extends String> and , for example List<String> parsing similarly at compile time or List<? extends String> parsing different from List<E> for some reifiable type E?

Generics are used to specify a type.
Consider the following class def:
public interface List<E> extends Collection<E>
This tells us there we have a List that can hold elements of any type.
Notice how there are no restrictions on E.
If we define a class like so:
public class MyList<T extends String> implements List<T>
We now have a MyList that implements List, but only accepts Strings (or decedents).
Inside this class we can refer to T.
public class MyList<T extends String> implements List<T> {
private ArrayList<T> internalStorage;
Key point
In the class definition we have defined what T is; it's any class based on String.
Inside the class T can thus be referenced.
However the flavor of T does not get fixed until the class actually gets instantiated:
MyList<MyStringType> test = new MyList<MyStringType>(parameters); //java 6
MyList<MyStringType> test = new MyList<>(parameters); //java 7, same but shorter.
Now Java knows what T means inside the MyList class T is a MyStringType.
Armed with is knowledge Java compiles the class and replaces all references to T with references to the actual class MyStringType.
It completely forgets about T.
Now inside the class note that everywhere T is mentioned it will get replaced by MyStringType.
But what if I want to handle a string, but not necessarily the MyStringType.
Solution:
I define a member like so:
List<? extends String> strs; //Will fill the data in later
Now we have a List called strs that will only accept strings, but that will not be forced to use strings of type MyStringType.
This list is not bound to the definition of T that was fixed when MyList was instantiated.
When we assign a value to the strs, the flavor of the List if fixed. In your example it will be a List of String.
? extends String str;//error
The variable cannot be fixed, because its type cannot be nailed down when the class that holds it gets created.

Generics is designed for read and not for write. This is why you can use generics in parameters for a method, but cannot use it for declare a variable that you can use after for read/write from it :-)

Related

Why is it necessary to extend in case of read and super in write in generics wildcards?

I am having a hard time to understand the concept of generics wild cards.
As per my understanding <?> type unknown is introduced to resolve the co-variance not supported in generics and it should fit any type of collection and <?extends T> means that you can have collection of types T or the class which extends T.<?super T> means you can have collection of types T or super(s) of T.
Please correct me, if the above is wrong.
When I try to write it like this:
import java.util.*;
public class Gclass {
static Gclass t;
public void write(List< ?super String > lw){
lw.add("b");
}
public void read(List< ? extends String> lr){
String s=lr.get(2);
System.out.println(s);
}
public static void main(String[] args) {
t=new Gclass();
List<String> l=new ArrayList<String>();
l.add("a");
l.add("");
System.out.println(l);
t.write(l);
System.out.println(l);
t.read(l);
System.out.println(l);
}
}
It works but my places of doubt are:
As per my understanding both (extends and super) includes the type declared, so in this particular case as my List is of type String. I could interchange the extends and super, but I get compilation error?
In case of write ? super Object is not working? It should work as it is super of String?
I did not check for read as String can not be extended, but I think I'm also missing a concept here.
I've read all answers on SO related to this problem, but am still not able to have a good understanding about it.
String is indeed a bit of a bad example as it is a final class, but consider something like Number instead.
If a method takes a parameter of type List<? extends Number> then you can pass it a List<Number> or a List<Integer> or a List<BigDecimal> etc. Within the method body it is therefore fine to take things out of the list (as you know they must be instances of Number) but you can't put anything in because you don't know whether or not it's safe (the compiler can't let you risk putting an Integer into a List<Float>, for example).
Conversely if the method takes List<? super Number> then you can pass it a List<Number> or List<Object> - you can't take anything out of this list because you don't know what type it is*, but you do know that it'll definitely be safe to put a Number in.
* technically you can take things out but the only type you can assign them to is Object
As per my understanding both(extends and super) includes the type declared(String here), so in this particular case as my List is of type String... I could interchange the extends and super but i get compilation error?
You're right that both ? extends String and ? super String includes String. But you are missing the point that, ? super String also includes CharSequence, Object, which is not in bounds of ? extends String. You can add a String to a List<? super String>, b'coz whatever type that list is of, it can definitely refer to a String. But, you cannot add say an Integer to a List<? extends Number>, because the list can be a List<Float> actually.
In case of write ? super Object is not working? It should work as it is super of String?
Object is a super class of String will fit in where you have ? super String, and use Object for that. So, ? super String can capture Object, but ? super Object cannot capture String, as String is not a super type of Object. Think of it like this: "Actual type replaces the ?, and it must satisfy the rules attached to that ?.
List<? super String> means that lw holds a value of List with type argument which is String or it's superclass, so you can add a String value "b" (because it can be casted to list's type argument).
List<? extends String> means that lw holds a value of List with type argument which is String or it's subclasses, so values from lw can be casted to String.

Wildcard java extends

Consider the following code:
List<? extends Integer> lst= new ArraList<Integer>();
lst.add(5);//Compile error
lst.get(5);//OK
In the second string we have compile error, because we must guaranted have method add(int) in all potencial subtypes of List<? extends Integer> and compiler know that its null only, third string returns unknow type and compiler cast him to Object, isnt it?
PECS. Producer extends, Consumer super.
List<? super Integer> lst= new ArrayList<Integer>();
lst.add(5);// No Compile error
The list is a consumer now, you put objects into it...
Also this
lst.get(5);
Is a different cake... You provide the Integer index of which you want to get... (Not to mention what Sotirios mentioned: the return type will be Object in this case. Also, in this role, the list is a provider...)
Once you have a List<? extends Integer>, the compiler doesn't know whether it's Integer or a subtype. The compiler can't ensure the type safety of adding anything except null (really, passing anything to a method taking a generic type parameter, including the add method), so it disallows it. This occurs despite the fact that Integer is final and no subclasses are allowed.
For the get method, the compiler knows that the return type is some kind of Integer, so it allows the call and places an implicit cast to Integer.
You cannot add anything except null to a List bounded by a wildcard because you never know the underlying type.
List<? extends Integer> lst= new ArrayList<Integer>();
lst.add(5); // Compile error
lst.get(5); // 5 is just the index
You can however get an element because you know it must be an Integer (in this case).
It's hard to explain with Integer because it's a class that cannot be extended. But take this
public class Foo {}
public class Bar extends Foo {}
public class Zoop extends Foo {}
What could you add to
List<? extends Foo> fooList = new ArrayList<Zoop>(); // could come from a method
?
The list is declared as ? extends Foo even though the runtime object's generic type is Zoop. The compiler therefore cannot let you add anything. But you are guaranteed to be operating on Foo objects, so you can retrieve them fine.

<? extends > Java syntax

This code:
List<? extends Reader> weirdList;
weirdList.add(new BufferedReader(null));
has a compile error of
The method add(capture#1-of ? extends Reader) in the type
List is not applicable for the
arguments (BufferedReader)
Why? BufferedReader extends reader, so why isn't that a "match"?
List<? extends Reader> weirdList can hold reference to any type of List that stores any type of Reader. So it is possible that
List<? extends Reader> weirdList1 = new ArrayList<BufferedReader>();
List<? extends Reader> weirdList2 = new ArrayList<FileReader>();
If Java would allow you to add BufferedReader to weirdList1 it would also have to let you add BufferedReader to weirdList2 (reference type is the same) which is not suppose to happen since weirdList2 should store only FileReaders.
When the compiler sees <? extends Reader>, then it assumes that it could be any type that is or extends Reader. It could be something that is imcompatible with BufferedReader, such as StringReader. So if the generic type in the class definition shows up in the parameter of a method such as add, and the type of the instance has something like <? extends Something>, the compiler must disallow it for type safety reasons. In this example, it could be List<StringReader>, so you shouldn't be able to add a BufferedReader here.
For the variable you gave:
List<? extends Reader> weirdList;
All of the following assignments are valid:
weirdList = new ArrayList<Reader>();
weirdList = new ArrayList<FileReader>();
weirdList = new ArrayList<BufferedReader>();
weirdList = new ArrayList<InputStreamReader>();
Hopefully this explains your compile error. What you're trying makes sense if weirdList holds a value of type ArrayList<BufferedReader>, but doesn't make sense for a value of type ArrayList<FileReader>. Since a variable of type List<? extends Reader> can hold a value of either type (and more!), Java calls that an error.
Generics in Java are hard to get your head around. You can think of the List<? extends Reader> type as being mostly useful for assignment or parameter types in methods so that they can accept a wide variety of types. For "regular use", you're probably better off with a "bare" generic like List<Reader> or even List<BufferedReader>.

Can the Object class really be a lower bound?

Why is the following legal when String & Integer are not super classes of Object ?
List<? super Object> mylist = new ArrayList<Object>();
mylist.add("Java"); // no compile error
mylist.add(2);
I'm aware that wild card guidelines use lower bounded wild cards and super for 'out' variables but it seems that Object doesn't function as a 'lower bound' in this case.
Also is this the only way to allow addition of any type into a list ?
It's really simple. Remember that in Java, an instance of a subtype is also an instance of its supertypes.
Look at the signature of add
public boolean add(E e)
That means whatever you pass something whose type is E or any subtype of E.
You have a List<? super Object>. So you can pass to myList.add() anything whose type is ? super Object (an unknown type which could be Object or supertype thereof) or any subtype thereof.
Is Integer a subtype of all types contained by ? super Object? Of course. Integer is a subtype of Object, which is a subtype of all types contained by ? super Object (of course, in this case, only Object satisfies this).
You're confusing the type parameter with the things you can pass to methods. The type argument of List<? super Object> is an unknown type that is a supertype of Object, so Integer or String cannot be the actual type parameter. In fact, in this case the only valid actual type argument would be Object. But what you're asking when you pass something to the method is, is the thing I'm passing a subtype? And the answer is yes.
I agree that it's confusing, but here's what's happening.
In this line of code:
List<? super Object> mylist...
You're saying that myList is a List, where each element can be of a type that is Object or a superclass of Object. However, you're only declaring the type of myList here.
What the wildcard does is restricts your implementation of myList.
Then, you do this:
List<? super Object> mylist = new ArrayList<Object>();
Now what you're doing is instantiating an ArrayList<Object>. Your lower bound wildcard is used to check that this is valid. It is valid, because Object matches ? super Object. At this point, you have a List<Object> and your ensuing method calls are permitted.
It's because Object is a superclass for Integer and String. You're interpreting the generic relationship the other way around.
Edit
Think about this situation:
List<? extends myClass> listOfMyClass = new ArrayList<Object>();
In this case, you'll end up with a list of Object type elements but that have to respect the restriction added by the declaration of the listOfMyClass list.
You'll be able to add any object that belongs to the myClass hierarchy to the list. The ArrayList that's implementing the List interface will hold (and return) Object type elements when requested.
Of course, you can define this:
List<? extends myClass> listOfMyClass = new ArrayList<mySuperClass>();
As you might now, the ArrayList must contain objects with the same type or a supertype of myClass and, in this case, that's the mySuperClass. This list will return mySuperClass objects qhen requested.
Taking ClassX as a class that does not belong to the mySuperClass hierarchy, the following line won't compile:
List<? extends myClass> listOfMyClass = new ArrayList<ClassX>();
That's because ClassX is not a superclass of myClass.

Java generics for Enum types

I am trying to write a Java class, one part of which requires a mapping of the values of an unknown enum to another class. My class contains a field private Map<? extends Enum, Unit> myMap and is initialized with a factory method createMyClass:
public static MyClass <T extends Enum> myClass createMyClass(Class<T> x) {
MyClass theClass = new MyClass() //constructor is private
//...
myMap = new HashMap<T, Unit>();
for(T t : x.getEnumConstants())
myMap.put(t, theClass.new Unit(t));
//...
}
The class Unit is (and needs to be, as far as I can tell) an inner class of MyClass. When I put this into NetBeans it complains with this message:
method put in interface java.util.Map<K,V> cannot be applied to given types
required: capture #4 of ? extends java.lang.Enum, MyClass.Unit
found: T, MyClass.Unit
I understand (or at least I think I do) how the Collections need to be very careful about wildcard usage to preserve type safety, but I can't think of how T extends Enum fails to match ? extends Enum.
Try changing your method declaration to:
public static <T extends Enum<T>> MyClass createMyClass(Class<T> x) {
}
What Map<? extends Enum, Unit> means is "The key is some specific class that extends Enum, but I don't known which one". And since you don't know which class it is, you cannot add anything to such a Map, you can only get elements from it and be certain they are Enums.
You should probably just declare that field as Map<Enum, Unit> - it will allow all Enum subclasses just fine.
This seems to be the most common misunderstanding about Java enums by a huge margin - people see that ? wildcard and think they have to use it to allow subclasses.
You can read this Generics Tutorial. It explains why this doesn't work in section 4 (Wildcards).
As far as I understand, ? extends Enum can be any enum, not just a T or subclass of T.

Categories