2014-09-18 37 views
2

返回的HashMap 我有幾個TSV文件,並想讀他們在 Java方法與動態對象

的一個HashMap填充[第一場 - 在一個對象>等領域。

爲了簡單起見,假設有兩個文件:

文件1包含兩個字段(字段1和字段2)。
文件2包含三個字段(f1,f2和f3)。

所以我定義了兩個班,其目的是要在HashMap中值:

Class1{ 
    String field1 = ""; 
    String field2 = ""; 
} 
Class2{ 
    String f1 = ""; 
    String f2 = ""; 
    String f3 = ""; 
} 

現在,我有以下方法:

public static HashMap<String, Class1> readTSV1(String fileName, Class1 c){ 
... 
} 

public static HashMap<String, Class2> readTSV2(String fileName, Class2 c){ 
... 
} 
... 

但我不想定義從不同的文件讀取各種方法:

我想要這樣的:

public static HashMap<String, Object> readTSV(String fileName, Class<?> c){ 
    HashMap<String, c.getClass()> hm = new HashMap<String, c.getClass()>(); //error. 
    //Look which field names are in type c, 
    //and then read two or three fields from file, 
    //and put them as key and values of hm (first field is key, other fields are put in instance of the respective class, and put as values) 
    return hm; 
} 

static void main(String[] args){ 
    Class1 c1; 
    HashMap<String, Class1> hm1 = new HashMap<String, Class1>(); 
    hm1 = readTSV("firstFile.tsv", c1.getClass()) 

    Class2 c2; 
    HashMap<String, Class2> hm1 = new HashMap<String, Class2>(); 
    hm1 = readTSV("firstFile.tsv", c2.getClass()) 

    ... 
} 

任何想法? ...

+3

聽起來像你真正想讓你的方法返回的是一個'HashMap >'。您可以將'int'傳遞給'readTSV'方法來說明要讀取多少個字段。或者,您可以繼續閱讀字段並將其添加到行末。 – 2014-09-18 19:39:14

+0

它可以工作,但如果適用,這個可讀性更高。 例如,在main中,我們可以說:(Class1)hm1.get(「field1」).field1而不是fields [i]。文件數量及其不同字段太多,可能會導致錯誤。 – Alisa 2014-09-18 19:41:14

+0

那麼,爲了完成你所描述的內容,你需要思考,看看你班上的領域。相信我,添加反射代碼後,它的可讀性會降低很多。 – 2014-09-18 19:42:35

回答

1

使用Hashmap<String, List<String>>可能是最簡單的方法。但是,如果你真的想要這些對象,你可以做一些接口。

public interface CSVConvertable { 
    /* sets values in this class according to a row in the CSV file */ 
    public void setCSVValues(String [] values); 
} 

class Class1 implements CSVConvertable { 
    String field1 = ""; 
    String field2 = ""; 
    @Override 
    public void setCSVValues(String[] values) { 
     field1 = values[0]; 
     field2 = values[1]; 
    } 
} 
class Class2 implements CSVConvertable { 
    String f1 = ""; 
    String f2 = ""; 
    String f3 = ""; 
    @Override 
    public void setCSVValues(String[] values) { 
     f1 = values[0]; 
     f2 = values[1]; 
     f3 = values[2]; 
    } 
} 

public static <T extends CSVConvertable> HashMap<String, T> readTSV(String fileName, Class<T> c) throws InstantiationException, IllegalAccessException{ 
    HashMap<String, T> hm = new HashMap<String, T>(); 
    while(/* read rows in csv*/) { 
     CSVConvertable conv = c.newInstance(); 
     conv.setCSVValues(/*your row array here*/); 
    } 

    return hm; 
} 


static void main(String[] args){ 
    HashMap<String, Class1> hm1 = new HashMap<String, Class1>(); 
    hm1 = readTSV("firstFile.tsv", Class1.class); 

    HashMap<String, Class2> hm2 = new HashMap<String, Class2>(); 
    hm2 = readTSV("firstFile.tsv", Class2.class); 

    ... 
} 

反思

如果你真的想在這裏使用反射是它的一個基本的實現。但是,您應該注意到,如果您向類中添加了新屬性,更改了屬性名稱或使類擴展了另一個類,則此實現將會發生變化。

public static <T> List<T> readTSV(String fileName, Class<T> c) throws InstantiationException, IllegalAccessException, IntrospectionException, NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException{ 
    List<T> list = new ArrayList<T>(); //error. 
    List<String> properties = getBeanProperties(c); 
    Collections.sort(properties); 

    // loop through all rows of the TSV and set each value 
    while(/*read rows in tsv*/) { 
     T obj = c.newInstance(); 
     for(int i=0;i<properties.size();i++) { 
      setProperty(obj, properties.get(i), /* get row column [i] */); 
     } 
     list.add(obj); 
    } 

    return list; 
} 


public static void main(String[] args) throws InstantiationException, IllegalAccessException, IntrospectionException, NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException{ 
    List<Class1> hm1 = readTSV("firstFile.tsv", Class1.class); 
    System.out.println(hm1); 

    List<Class2> hm2 = readTSV("firstFile.tsv", Class2.class); 
    System.out.println(hm2); 

} 

public static void setProperty(Object obj, String propName, Object value) throws NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
    String setterName = "set" + propName.substring(0, 1).toUpperCase() 
      + propName.substring(1); 

    Field field = obj.getClass().getDeclaredField(propName); 
    if(Modifier.isPrivate(field.getModifiers())) { 
     Method method = obj.getClass().getMethod(setterName, field.getType()); 
     method.invoke(obj, value); 
    } else { 
     field.set(obj, value); 
    } 
} 

public static List<String> getBeanProperties(Class<?> cl) { 
    List<String> properties = new ArrayList<String>(); 
    // check all declared fields 
    for (Field field : cl.getDeclaredFields()) { 
     // if field is private then look for setters/getters 
     if (Modifier.isPrivate(field.getModifiers())) { 
      // changing 1st letter to upper case 
      String name = field.getName(); 
      String upperCaseName = name.substring(0, 1).toUpperCase() 
        + name.substring(1); 
      // and have getter and setter 
      try { 
       String simpleType = field.getType().getSimpleName(); 
       //for boolean property methods should be isProperty and setProperty(propertyType) 
       if (simpleType.equals("Boolean") || simpleType.equals("boolean")) { 
        if ((cl.getDeclaredMethod("is" + upperCaseName) != null) 
          && (cl.getDeclaredMethod("set" + upperCaseName, 
            field.getType()) != null)) { 
        } 
        properties.add(name); 
       } 
       //for not boolean property methods should be getProperty and setProperty(propertyType) 
       else { 
        if ((cl.getDeclaredMethod("get" + upperCaseName) != null) 
          && (cl.getDeclaredMethod("set" + upperCaseName, 
            field.getType()) != null)) { 
        } 
        properties.add(name); 
       } 
      } catch (NoSuchMethodException | SecurityException e) { 
       // if there is no method nothing bad will happen 
      } 
     } else { 
      // Accessible property that isnt defined by the jre 
      if(!field.isSynthetic()) { 
       properties.add(field.getName()); 
      } 
     } 
    } 
    return properties; 
} 
+0

謝謝。它實際上工作,但我希望類「Class1實現CSVConvertable」自動執行此分配。我想動態地分配類的字段(不提他們的名字)。讓我說,班級中的字段名稱是按字母順序排列的(並且與TSV的順序相同)。所以我們可以通過getNextProperty()。value()等方法訪問它們。 – Alisa 2014-09-18 20:43:23

+1

@Alisa你可以用反射和按名稱對屬性進行排序來完成它,但不能按它們在源代碼中聲明的順序對它們進行排序。這是因爲java是一種編譯語言。所以如果你不想要界面,那麼你的下一個最好的賭注是使用註釋。如果你甚至不想使用註釋,你可以使用一些反射來獲得所有的字段名稱。我強烈建議不要使用反射來獲得屬性,除非你真的知道你在做什麼。 – 2014-09-18 21:04:09

+0

@Alisa我加了一個反射實現的例子 – 2014-09-18 21:39:38

1

您可以使用繼承

標記接口

public interface ForClasses{ 
} 

Class1 implements ForClasses { 
    ... 
} 
Class2 implements ForClasses{ 
    ... 
} 

然後,你可以這樣做:

HashMap<String, ForClasses > hm = new HashMap<String, ForClasses>(); 

HM可容納在地圖價值部分都class1的對象和類class2對象...

+0

但是要真正使用它們,你需要施放...我更喜歡David的方法。 – Fildor 2014-09-18 19:45:58

+0

沒關係,但是如何尋找類的字段名稱(從文件讀取)。我的意思是該方法中的三條註釋行。 – Alisa 2014-09-18 19:48:26