Skip to main content

Generics wild card in java

List<Double> or List<Float> cannot be assigned to List<Number> because, List<Double> isn't extend List<Number>.
List<Double> dblList = new ArrayList<>();
List<Number> numList = dblList; //illegal
To do that we need to use upper-bound wild card.
List<Double> dblList = new ArrayList<>();
List<? extends Number> numList = dblList;
Same way List<Number> isn't extend List<Object> so, List<Object> cannot be assigned to List<Number>
List<Object> objList = new ArrayList<>();
List<Number> numList = objList; //illegal
To do that we need to use lower-bound wild card
List<Object> objList = new ArrayList<>();
List<? super Number> numList = objList;

Upper-bound

We can add object of Double type or its sub types to List<Double>.
Same way we can add Number or its sub types (e.g. Double, Float, Integer) to List<Number> but, we cannot assign dblList to numList. Why?
The reason is that we can add any sub type of Number to numList and numList is just a reference of dblList so that sub type would actually be added in dblList, if that sub-type is Integer or Float than it is problem.
List<Number> numList = dblList; //Suppose this is possible then
numList.add(new Integer(1)); // this is not solving purpose of List<Double>
numList.add(new Float(3.2)); //same as above
That's why above assignment made illegal by java compiler.
Now if we do this...
List<? extends Number> numList = dblList; // Legal
then also above scenario would be happenned... so, compiler made illegal to accept value in any of the method where type declared with <? extends SomeClass>
class MyContainer<T> {
    ...
    public void accept(T value) {
        ...
    }
    ...
}
If we declare reference of MyContainer<? extends Number> then any method like "accept" which accepts "T" as parameter is illegal on that reference.
numList.add(new Integer(1)); //illegal
numList.add(new Double(2.34)); //illegal!
So, what's the use of upper-bound wild card?
It is used to get data from container, not to add data to container.
When we do like this..
Number value = numList.get(0);
We are sure here that at least we get Number from numList because it has Number or sub-types of it but, if compiler allow to call add on numList reference then below situation can be occurred.
List<? extends Number> numList = dblList;
numList.add(new Integer(4)); //This is not logical, so compiler don't want to arise this situation

Lower-bound

Lower-bond is used to add data to container but when we try to get data from container it returns us Object.
Let's see why.
List<Object> objList = new ArrayList<>();
List<? super Number> numList = objList;
Here it doesn't mean we can add Number or any of its super type to container. No.
We only allowed to add Number and its sub-types to numList then, what's use of it?

We can pass List<Object>, List<Serializable> to method which accepts List<? super Number>.
That way once the method finishes adding values to container we have a container to perform specific operation on.
public void addValues(List<? super Number> numList) {
   for(int i = 0; i < 10; i ++) {
       numList.add(i);
   }
}

public void customSerializationOfList(List<Serializable> serList) {
   for(Serializable ser : serList) {      
      ...
      ObjectOutputStream oos = ...
      oos.writeObject(ser);
      ...
   }
}

public void customPrintingOfList(List<Object> objList) {
   for(Object obj : objList) {
      System.out.println("**** Obj: "+obj.toString()+" ****");
   }
}
public void operateForObjectList() {
   List<Object> objList = new ArrayList<>();
   addValues(objList);
   customPrintingOfList(objList);
}

public void operateForSerializableList() {
   List<Serializable> serList = new ArrayList<>();
   addValues(serList);
   customSerializationOfList(serList);
}
Here, our purpose is to use general method customSerializationOfList which can be used from any other part of application with List<String>, List<Color> etc. Here, if we try to get value from numList it always returns Object type because, it doesn't know which super type of Number the container is of. 
It can be any super type of list as shown in above case so it always resolves to base class Object.

Unbounded

Unbounded type is unknown type with no binding information.
List<?> unList = new ArrayList<>();
Any kind of List can be assigned to unList reference. We cannot add value to container pointed by unList type of reference and if we try to get value from unList type of reference it always resolves to Object.

This is used when we want value of container just to be Object but we cannot declare it as List<Object> specifically
public List<? super Number> getList() {
    ...
}

public void doSomethingOnList(List<?> objList) {
   for(Object obj : objList) {
      //do something on each obj
   }
}

List<?> unList = getList();
doSomethingOnList(unList);
We can pass reference of List<Object> to doSomethingOnList method
List<Object> objList = new ArrayList<>();
doSomethingOnList(objList);
but,
List<Object> objList = getList(); //illegal

Comments

Popular posts from this blog

Basics of quantum computing: change of basis

Computing requires classical bits, which can hold a value of 0 or 1. Quantum computing gets benefit from superposition of quantum bit (qubit). Qubit is a representation of state of quantum object (electron, proton, photon, etc). The qubit holds a value between 0 and 1 while it is in superposition state. Superposition means the qubit possesses multiple values at the same time so we can harness its power for parallel computing. Once we try to measure the value it can be collapsed into either 0 or 1 based on probability. (Observer collapses wave function simply by observing.) Using bloch sphere, we can represent qubit physically. (The bloch sphere is a physical model to represent a spin state of qubit) A state vector can point to any direction from the center of the sphere. If a vector points to Z+ and Z-, it represents 0 and 1 state respectively. All other vectors represent superposition state in a Z basis. Now, if a vector points to X+, X-, Y+, Y-, all are at a 90-degree angle from

Monad: in programming

Monad is a structure with type constructor M and two functions unit ( η) and multiplication ( μ). and   To lift value from a to Ma we need unit function and to flatten from M(Ma) to Ma we need multiplication . Why do we need monad in functional programming? Why do we need functional programming anyways? Functional programming is a paradigm which uses pure functions to build an application. The power of pure functions are, we can compose them and we can build complex things out of the small and simple functions. If we have these functions   and   We can get   We can use below general purpose compose function to compose any two pure functions.   If we need to compose three functions then we can do like Now, we can't build application using only pure functions. It is next to impossible. We need side effects anyways to make the application practical in use. To achieve the goal, we need to keep pure functions in center/core part of application and need to move side effects outsid

Monad: to programming from category theory

We have seen the summarized article "monad in programming" in the previous post. Let's find out what is a monad in category theory and how the concept became relevant for functional programming. First, we need to understand the basics of category theory. Category A category consists of objects and morphisms (relationships) between those objects. More of it, the morphisms should be composable too. Below is an example of a category. Each dot is an object and each arrow shows morphism from some object to another. As arrows are composable, if there is a morphism from object a to object b , and there is another morphism from b to c , then we can find a morphism from a to c .  If you notice, there is identity morphism too, it exists for each object, but we have shown it just for x . It is a morphism from an object to itself. If we compose identity morphism with any other morphism w , we get the same w in return. Functor A functor is a relationship between categories