I'm working on some set of abstractions representing an SQL result set and have written this methods:
public interface Column<T>{
//some methods
}
public class SqlRowExtractor{
public void doExtraction(){
Collection<Column<?>> cols = getColumns(); //Returns the collection of Column<T>.
//A particular result set may contain,
//for instance 2 Column<Date>,
//1 Column<Integer> and so fotrh
for(Column<?> c : cols){
put(c, get(c)); //1 <-------------------- HERE
//Compile-error
}
//other staff
}
public <T> void put(Column<T> c, T o){
//put the extracted value in another place
}
public <T> T get(Column<T> c) throws SQLException{
//extract the value of the row for the Column<T> c
}
}
I got compile-error at //1
, although it's perfectly clear by the signature of put
and get
methods that there is no way to put inaproppriate value. How can I fix the error in a type-safe way? The error message:
The method put(Column<T>, T) is not applicable for the arguments
(Column<capture#3-of ?>, capture#4-of ?)
This does not compile because the compiler does not understand that the use of the wild-card type for the get
call is the same type as the use of a wild-card type in for the set
call, even if the methods are used with the same object.
You can solve it by introducing a util method with a type parameter. In that way, the wild-card type is used only once, and inside the method the type parameters will have a concrete type, which the compiler can understand that it is the same one that is used in multiple places.
The concrete type which is assigned to a wild-card type in each separate place where it is used is called the capture of the wild-card type, and it is given a name like capture#3-of ?
by the compiler.
The following compiles:
private <T> void getPut(Column<T> c) throws SQLException {
// Compiles because the type's capture, which in non-wildcard, has been bound to T
put(c, get(c));
}
public void doExtraction() throws SQLException {
Collection<Column<?>> cols = getColumns();
for(Column<?> c : cols) {
// Compiles because wild-card type of c is only used once
getPut(c);
}
}
The same technique is used in an example of capturing conversion in the JLS.