An example: Let us assume, we have the following example program.
package com.geekyarticles.java;
import java.util.ArrayList;
import java.util.List;
public class CaptureExample<E, F extends E> {
public void copy(List<E> toGet, List<F> copyFrom){
for(F xcopyFrom){
toGet.add(x);
}
}
public static void main(String [] args){
List<Object> aList=new ArrayList<Object>();
List<String> copyFrom=new ArrayList<String>();
copyFrom.add("Hi");
CaptureExample<Object, String> ce=new CaptureExample<Object, String>();//Here is where the capture conversion takes place
ce.copy(aList, copyFrom);
System.out.println(aList);
}
}
import java.util.ArrayList;
import java.util.List;
public class CaptureExample<E, F extends E> {
public void copy(List<E> toGet, List<F> copyFrom){
for(F xcopyFrom){
toGet.add(x);
}
}
public static void main(String [] args){
List<Object> aList=new ArrayList<Object>();
List<String> copyFrom=new ArrayList<String>();
copyFrom.add("Hi");
CaptureExample<Object, String> ce=new CaptureExample<Object, String>();//Here is where the capture conversion takes place
ce.copy(aList, copyFrom);
System.out.println(aList);
}
}
In the above example, an instance of CaptureExample is created passing the arguments Object and String. After capture conversion, a new type is created where E is replaced by Object and F is replaced by String. So the method copy is now copy(List<Object>,List<String>. Pretty simple. Let's check out the other three cases where wild cards are used.
Unbounded wildcard: If we use unbounded wildcard (?) while creating the reference of a generic type, the capture conversion will replace the type parameter with a type which has an upper bound same as the type parameter itself and lower bound null type.
For example, let's consider the following example.
package com.geekyarticles.java;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class CaptureExampleUnboundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
CaptureExampleUnboundedWildcard<?> ce=new CaptureExampleUnboundedWildcard<InputStream>();
Object obj=new Object();
ce.readFromIt(obj);//This is an error, because the capture converted type has an upper bound InputStream from the original declaration
}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class CaptureExampleUnboundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
CaptureExampleUnboundedWildcard<?> ce=new CaptureExampleUnboundedWildcard<InputStream>();
Object obj=new Object();
ce.readFromIt(obj);//This is an error, because the capture converted type has an upper bound InputStream from the original declaration
}
}
In this case, the type E is converted to ? extends InputStream, and hence it now the method cannot be invoked with an object of type Object. However, in this case the method readFromIt() simply cannot be invoked on the reference ce. I will discuss this in the following note.
Note: If after capture conversion, an argument of a method does not have a lower bound, the method simply
cannot be invoked. This is because, since the argument type does not have a lower bound, there exists no type that the compiler can guarantee to
be a subtype (as required by method invocation conversion) of the capture converted type.
Upper bounded wildcard: If the wildcard used during creation of a reference is upper bounded (like E extends SomeType), the resultant type will have both the upper bound as passed and as the original declaration.
package com.geekyarticles.java;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
public class CaptureExampleUpperBoundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? extends Serializable as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
//In this case even this is a compile time error. because the type argument must be both InputStream and Serializable
CaptureExampleUpperBoundedWildcard<? extends Serializable> ce=new CaptureExampleUpperBoundedWildcard<InputStream>();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
public class CaptureExampleUpperBoundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? extends Serializable as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
//In this case even this is a compile time error. because the type argument must be both InputStream and Serializable
CaptureExampleUpperBoundedWildcard<? extends Serializable> ce=new CaptureExampleUpperBoundedWildcard<InputStream>();
}
}
How to declare a type parameter extends multiple type: If the type parameter in a generic type declaration
need to extend more than one type, the types can be separated using the & operator. For example,
public class CaptureExampleUpperBoundedWildcard<E extends InputStream & Comparable> {
...
}
public class CaptureExampleUpperBoundedWildcard<E extends InputStream & Comparable> {
...
}
Lower bounded wildcard: Lower bounded wildcards are of the form (? super SomeType). If this is used in the reference creation, the capture converted type will have the upper bound as originally declared and the lower bound SomeType. Let's consider the following example.
package com.geekyarticles.java;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class CaptureExampleLowerBoundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? super FilterInputStream as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
CaptureExampleUnboundedWildcard<? super FilterInputStream> ce=new CaptureExampleUnboundedWildcard<InputStream>();
BufferedInputStream obj=new BufferedInputStream(new FileInputStream("somefile"));
//Now we can call this without an error, because the capture converted type does have a lower bound.
//Note that subclasses of the lower bound are allowed in method invocation due normal method invocation
//conversion
ce.readFromIt(obj);
}
}
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class CaptureExampleLowerBoundedWildcard<E extends InputStream> {
public void readFromIt(E readFrom) throws IOException{
readFrom.read();
}
public static void main(String [] args) throws Exception{
//The ce is passed only ? super FilterInputStream as a type argument. Hence the capture converted type has an upper
//bound same as the original bound during the declaration, which is InputStream
//Note that it is not possible to create an Object of wildcard type. That is a compile
//time error
CaptureExampleUnboundedWildcard<? super FilterInputStream> ce=new CaptureExampleUnboundedWildcard<InputStream>();
BufferedInputStream obj=new BufferedInputStream(new FileInputStream("somefile"));
//Now we can call this without an error, because the capture converted type does have a lower bound.
//Note that subclasses of the lower bound are allowed in method invocation due normal method invocation
//conversion
ce.readFromIt(obj);
}
}
In this case the lower bound is FilterInputStream and the upper bound is InputStream.
very nice post.thanks to sharing this post.
ReplyDelete