即使使用泛型我也区分了T型

时间:2017-06-12 13:44:29

标签: java generics

我想检查一个Excel .csv列,它可以是String,Int oder Double类型。我实现了一个经典的通用Pair类:

public class PairT<K,V> implements Comparable<PairT<K,V>>

如果是整数列,则列值存储在:

ArrayList<PairT<Integer,Integer>> column_list = new ArrayList<>();

其中V值包含excel行索引。

下面的代码片段显示了令人讨厌的解决方案 - 我会改进:

// Add a cell value of type T to the column list
@SuppressWarnings("unchecked")
public static <T> void addCell(
        String excelcell,
        int row_idx,
        boolean ignorecase,
        T defkey,
        /*IO*/ArrayList<PairT<T,Integer>> column_list) throws RuntimeException
{   
    //Class<?> classtype = defkey.getClass();  String typename = classtype.getSimpleName();
    char type;
    if (defkey      instanceof String) type = 'S';
    else if (defkey instanceof Integer) type = 'I';
    else if (defkey instanceof Double) type = 'D';
    else type = 'O'; // other

    T key;
try
{
    switch(type)
    {
    case 'I': 
        try 
        {  key = (T)new Integer(excelcell);
        } catch (NumberFormatException e) { key = defkey; }  

        column_list.add(new PairT<T,Integer>(key,row_idx));
        break;         
    case 'D':
        try 
        {   key = (T)new Double(excelcell);          
        } catch (NumberFormatException e) { key = defkey; } 

        column_list.add(new PairT<T,Integer>(key,row_idx));
        break;
    case 'S':
        if (ignorecase) excelcell = excelcell.toUpperCase();
        column_list.add(new PairT<T,Integer>((T)excelcell,row_idx));
        break;
    default:  // Other take the .toString() output as key 
        column_list.add(new PairT<T,Integer>((T)excelcell.toString(),row_idx));
    }
}catch (Exception ex) // possibly a ClassCastException
{
    throw new RuntimeException("addCell(): Problems using PairT<K,V>",ex);
}
} //----- end of addCell()

测试我使用:

ArrayList<PairT<Integer,Integer>> column_list = new ArrayList<>();

int row_idx = 0; 

boolean ic = true; // for String values only;
Integer defval = new Integer("0");

String cell = "12";
addCell(cell,row_idx,ic,defval,column_list);

cell = "17.34"; // leads to def val
addCell(cell,++row_idx,ic,defval,column_list);

cell = "456";
addCell(cell,++row_idx,ic,defval,column_list);

cell = "foo"; // lead to def avlue
addCell(cell,++row_idx,ic,defval,column_list);

System.out.println("result: " + column_list); 
// [12;0, 0;1, 456;2, 0;3]

java.util.Collections.sort(column_list);  
System.out.println("Sorted: " + column_list); 

//Sorted: [0;1, 0;3, 12;0, 456;2]

它按预期工作,但是 - 我说 - 我不想区分addCell()中的Type T. 我更喜欢简短的解决方案,例如:

if (ignorecase) column_list.add(new PairT<T,Integer>((T)excelcell.toUpperCase(),row_idx));
else            column_list.add(new PairT<T,Integer>((T)excelcell,row_idx));

2 个答案:

答案 0 :(得分:1)

您的代码中存在一些问题;

首先,您将column_list定义为Integer值对的列表。您希望将其定义为ArrayList<PairT<T, Integer>> column_list = new ArrayList<>();,以允许存储String,Double或Integer数据。

其次,在addCell()中,检查整数defval的类型,它的值始终为0.然后在以下switch语句中使用从此变量推断的类型,这意味着您执行Integer的代码,无论excelCell的类型是什么。

考虑到这些因素,我使用泛型类型参数清理了代码addCell()

public class Main {

    @SuppressWarnings("unchecked")
    public static <T> void main(String[] args) {
        ArrayList<PairT<T, Integer>> list = new ArrayList<>();

        int row_idx = 0;

        String cell = "12";
        addCell((T)cell, row_idx, list);

        cell = "17.34";
        addCell((T)cell, ++row_idx, list);

        cell = "456";
        addCell((T)cell, ++row_idx, list);

        cell = "foo";
        addCell((T)cell, ++row_idx, list);

        System.out.println("result: " + list);
        java.util.Collections.sort(list);
        System.out.println("Sorted: " + list);

    }

    @SuppressWarnings("unchecked")
    public static <T> void addCell(T excelCell, int row_idx, ArrayList<PairT<T, Integer>> list){
        if(excelCell instanceof String) list.add((PairT<T, Integer>) new PairT<>(((String) excelCell).toUpperCase(), row_idx));
        else list.add(new PairT<>(excelCell, row_idx));
    }

我已移除ignoreCasedefval,因为它们与此示例无关。

答案 1 :(得分:0)

您可以使用我在my answer here中描述的内容并将Class<T>映射到Function<String, T>,但我倾向于同意VGR的评论,即您应该重新考虑使用方式仿制药在这里。

重构代码的一种简单方法,我认为VGR建议,就像这样:

public static void addCell
   (String  excelCell,
    int     rowIdx,
    boolean ignoreCase,
    Integer defCellValue,
    List<PairT<Integer, Integer>> columnList)
{
    Integer cellValue;
    try {
        cellValue = Integer.valueOf(excelCell);
    } catch (NumberFormatException x) {
        cellValue = defCellValue;
    }
    columnList.add(new PairT<>(cellValue, rowIdx));
}

// addCell overload with Double defCellValue
// addCell overload with String defCellValue

可以一般化并做一些这样的事情,虽然如果你只有几个重载它不会那么多:

public static void addCell
   (String  excelCell,
    int     rowIdx,
    boolean ignoreCase,
    Integer defCellValue,
    List<PairT<Integer, Integer>> columnList)
{
    addCell(excellCell,
            rowIdx,
            ignoreCase,
            defCellValue,
            columnList,
            Integer::valueOf);
}

public static void addCell(..., Double defCellValue, ...) {
    addCell(..., Double::valueOf);
}

public static void addCell(..., String defCellValue, ...) {
    addCell(..., Function.identity());
}

private static <T> void addCell
   (String  excelCell,
    int     rowIdx,
    boolean ignoreCase,
    T       defCellValue,
    List<PairT<T, Integer>> columnList,
    Function<String, T>     cellParser)
{
    if (ignoreCase) {
        excelCell = excelCell.toUpperCase();
    }
    T cellValue;
    try {
        cellValue = cellParser.apply(excelCell);
    } catch (IllegalArgumentException x) {
        cellValue = defValue;
    }
    columnList.add(new PairT<>(cellValue, rowIdx));
}

其他一些事情:

  • 写下例如更有意义一个class Cell<T> { int row, col; T value; }而不是尝试使用一对类。对类非常嘈杂,难以使用,你不能在对类中编写特定于单元格的实用方法。

  • 您应该坚持使用Java命名约定。静态常量为大写,空格为下划线(static final int FOO_COUNT;),所有其他变量均为驼峰式,第一个字母为小写(int fooCount;)。