2

我正在研究一组代表 SQL 结果集的抽象,并编写了以下方法:

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
    }
}

我在 处得到了编译错误//1,尽管通过putget方法的签名非常清楚,没有办法放置不适当的值。如何以类型安全的方式修复错误?错误信息:

The method put(Column<T>, T) is not applicable for the arguments 
(Column<capture#3-of ?>, capture#4-of ?)
4

3 回答 3

1

你必须重新设计你的代码,因为在那种形式下它不能编译。您的代码对于编译器来说不是类型安全的。你可以写成下面的形式

interface Column<T>{

    /**
     * extracts valu from the column
     * 
     * @return the value
     */
    T value() throws SQLException;
}

public class test{

    public void doExtraction() throws SQLException {
        Collection<Column<?>> cols = getColumns(); //Returns the collection of Column<T>.
        for(Column c : cols){
            put(c, c.value());
        }
    }

    public <T> void put(Column<T> c, T o){
        //put the extracted value in another place
    }

}

此代码有效,每列负责值提取

于 2015-10-31T11:28:56.453 回答
1

我不确定您到底想做什么,但对我来说,如果您想让所有内容类型安全,那么您需要传入列的类型。确保它们都使用相同的类型。

interface Column<T>{
    //some methods
}

class SqlRowExtractor{

    public <T> Collection<Column<T>> getColumns(Class<T> clss) {
        return null;
    }
    public <T> void doExtraction(Class<T> clss) throws SQLException{
        // Type T needs to be specified before this so compiler 
        // can check it.
        Collection<Column<T>> cols = getColumns(clss); 
        for(Column<T> c : cols){
            put(c, get(c)); 
        }
        //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
        return null;
    }
}
于 2015-10-31T11:05:16.863 回答
1

这不会编译,因为编译器不理解调用中使用通配符类型与get调用中使用通配符类型的类型相同set,即使方法与相同的对象一起使用.

您可以通过引入带有类型参数的 util 方法来解决它。这样,通配符类型只使用一次,并且在方法内部,类型参数将具有具体类型,编译器可以理解为在多个地方使用的都是同一个类型。

在每个单独使用它的地方分配给通配符类型的具体类型称为通配符类型的捕获,它capture#3-of ?由编译器给定一个名称。

以下编译:

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);  
    }
}

在 JLS 中捕获转换的示例中使用了相同的技术。

于 2015-10-31T11:41:35.770 回答