【问题标题】:Smarter way to write similar line of code编写类似代码行的更聪明的方法
【发布时间】:2015-12-27 16:34:59
【问题描述】:

假设我有一个初始化方法,其中有很多初始化。这是一个例子:

public void initialize(URL url, ResourceBundle rb) {

    date.setCellValueFactory(new PropertyValueFactory("date"));
    site_address.setCellValueFactory(new PropertyValueFactory("site_address"));
    bill_no.setCellValueFactory(new PropertyValueFactory("bill_no"));
    product_name.setCellValueFactory(new PropertyValueFactory("product_name"));
    shade.setCellValueFactory(new PropertyValueFactory("shade"));
    size.setCellValueFactory(new PropertyValueFactory("size"));
    mrp.setCellValueFactory(new PropertyValueFactory("mrp"));
    qty.setCellValueFactory(new PropertyValueFactory("qty"));
    less.setCellValueFactory(new PropertyValueFactory("less"));
    amount.setCellValueFactory(new PropertyValueFactory("amount"));


}

通常我写第一行 date.setCellValueFactory(new PropertyValueFactory("date")); 并复制该行并修改其他行。 这对我来说似乎很奇怪,并且正在考虑一种更好的方法来做到这一点。我的 IDE 是 NetBeans

还有其他更聪明的方法来处理这个问题吗?

【问题讨论】:

    标签: java javafx netbeans-8


    【解决方案1】:

    据我所知,没有特别优雅的方法可以做到这一点(除了为手头的任务创建很可能过度杀伤的结构),无论你做什么,在某些时候你必须输入属性和将它们与每一列相关联。我真的不建议继承TableColumn 或生成实现自定义接口的包装类,这些接口已经在标准API 中定义了功能。

    我通常只是通过编写一个方便的方法并调用它来节省多余的代码。基本思路是

    private void configColumn(TableColumn<?,?> column, String property) {
        column.setCellValueFactory(new PropertyValueFactory<>(property));
    }
    

    然后你的初始化就变成了

    configColumn(date, "date");
    configColumn(site_address, "site_address");
    // etc ...
    

    提高效率的诀窍是一开始就给列起一个很短的名字:

    private void c(TableColumn<?,?> column, String property) {
        column.setCellValueFactory(new PropertyValueFactory<>(property));
    }
    

    现在您要输入的内容要少得多:

    c(date, "date");
    c(site_address, "site_address");
    // ...
    

    然后一旦你把所有的东西都放进去,使用你的 IDE 将方法重命名为更易读的东西(我使用 Eclipse,所以你点击定义中的方法名称,选择“重构”和“重命名”,然后然后输入新名称。我假设 NetBeans 具有类似的功能。)不要省略这部分,否则你的代码会很难阅读。

    如果您真的想使用某种循环来执行此操作,那么假设您的 TableColumns 都具有与相应属性相同的名称,您可以使用反射,但我再次认为您在可读性方面的损失多于获得的简洁:

    public void initialize() throws Exception {
        List<String> colNames = Arrays.asList("date", "site_address", "bill_no" /*, ...*/);
    
        for (String colName : colNames) {
            Field f = this.getClass().getDeclaredField(colName);
            TableColumn<?,?> column = (TableColumn<?,?>) f.get(this);
            column.setCellValueFactory(new PropertyValueFactory(colName));
        }
    }
    

    注意还有另一种方法,但是(没有一些相当难看的接线)你只能在你可以访问 FXMLLoader 的地方使用这个技巧(所以你不能在控制器中这样做,只能在你所在的代码中加载 FXML 文件)。 FXMLLoader 提供对namespace 的访问权限,这是所有fx:id 属性值和在FXML 中创建的对象之间的映射(请注意,这里还有一些其他键值对,而不仅仅是那些用fx:id 定义)。因此,再次假设您的所有 TableColumns 具有与属性值匹配的 fx:id 属性值,您可以这样做:

    FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file"));
    Parent root = loader.load();
    Map<String, Object> namespace = loader.getNamespace();
    for (String fxid : namespace.keySet()) {
        Object value = namespace.get(fxid);
        if (value instanceof TableColumn) {
            ((TableColumn<?,?>)value).setCellValueFactory(new PropertyValueFactory(fxid));
        }
    }
    

    【讨论】:

      【解决方案2】:

      由于它们都具有相同的方法,并且该方法接收相同的type,因此可能的解决方案是创建一个以setCellValueFactory(PropertyValueFactory pvf) 作为其唯一方法的接口。

      然后将参数添加到数组中并遍历数组并设置其参数:

      public interface CVF{
      
          public void setCellValueFactory(PropertyValueFactory pvf);
      }
      

      那么你的每个对象,即date,site_address, etc . . . 应该是implement CVFoverride setCellValueFactory(PropertyValueFactory pvf);

      public void initialize(URL url, ResourceBundle rb) {
      
        ArrayList<CVF> objs = new ArrayList<>();
        objs.add(date);
        objs.add(site_address);
      // the rest of them . . . 
      
      
        String[] params = {"date","site_address","etc . . ."};
      
      // keeping in mind the ORDER of insertion for both objs and params
      
        for(int i=0; i< objs.size(); i++){
            objs.get(i).setCellValueFactory(new PropertyValueFactory(params[i]));
        }
      
      }
      

      甚至向CVF添加第二个方法

      public interface CVF{
      
          public void setCellValueFactory(PropertyValueFactory pvf);
          public String getParam();
      }
      

      然后我们以name 对象为例:

      public class Name implements CVF{
      
          @override
          public void setCellValueFactory(PropertyValueFactory pvf){
              // your code . . . 
          }
      
          @override
          public String getParam(){
            return "name"; // this would change for each CVF
         }
      }
      

      然后:

      public void initialize(URL url, ResourceBundle rb) {
      
        ArrayList<CVF> objs = new ArrayList<>();
        objs.add(date);
        objs.add(site_address);
      // the rest of them . . .              
      
        for(CVF o : objs){
            o.setCellValueFactory(new PropertyValueFactory(o.getParam());
        }
      
      }
      

      【讨论】:

      • 也许我完全误解了OP的问题,但由于这些是标准API方法,我假设所有对象都只是TableColumns,在这种情况下,额外的接口只是多余的。
      【解决方案3】:

      第一个问题是,您的变量(日期、站点地址等)实例是否属于同一类型?

      如果是,您可以使用对象和值创建地图,然后对其进行迭代(使用 guava 的 ImmutableMap):

      ImmutableMap<SomeCellObject,String> initializationMap = ImmutableMap.of(
              date, "date", 
              site_address, "site_address"
              // more ...
              );
      for (Map.Entry<SomeCellObject, String> entry : initializationMap.entrySet()) {
          entry.getKey().setCellValueFactory(new PropertyValueFactory(entry.getValue()));
      }
      

      如果没有,您必须尝试在 Java 中进行鸭式输入。由于 Java 是静态类型语言,这比动态类型语言(如 Python)更难。 Wikipedia 中提供了一个很好的例子:

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      public class DuckTyping {
      
          interface Walkable  { void walk(); }
          interface Swimmable { void swim(); }
          interface Quackable { void quack(); }
      
          public static void main(String[] args) {
              Duck d = new Duck();
              Person p = new Person();
      
              as(Walkable.class, d).walk();   //OK, duck has walk() method
              as(Swimmable.class, d).swim();  //OK, duck has swim() method
              as(Quackable.class, d).quack(); //OK, duck has quack() method
      
              as(Walkable.class, p).walk();   //OK, person has walk() method
              as(Swimmable.class, p).swim();  //OK, person has swim() method
              as(Quackable.class, p).quack(); //Runtime Error, person does not have quack() method
          }
      
          @SuppressWarnings("unchecked")
          static <T> T as(Class<T> t, final Object obj) {
              return (T) Proxy.newProxyInstance(t.getClassLoader(), new Class[] {t},
                  new InvocationHandler() {
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          try {
                              return obj.getClass()
                                  .getMethod(method.getName(), method.getParameterTypes())
                                  .invoke(obj, args);
                          } catch (NoSuchMethodException nsme) {
                              throw new NoSuchMethodError(nsme.getMessage());
                          } catch (InvocationTargetException ite) {
                              throw ite.getTargetException();
                          }
                      }
                  });
          }
      }
      
      class Duck {
          public void walk()  {System.out.println("I'm Duck, I can walk...");}
          public void swim()  {System.out.println("I'm Duck, I can swim...");}
          public void quack() {System.out.println("I'm Duck, I can quack...");}
      }
      
      class Person {
          public void walk()  {System.out.println("I'm Person, I can walk...");}
          public void swim()  {System.out.println("I'm Person, I can swim...");}
          public void talk()  {System.out.println("I'm Person, I can talk...");}
      }
      

      原则上,esteban 的回答很好,但是这段代码对您的应用程序更透明(您的类不必实现相同的接口)。

      【讨论】:

      • 我认为您可能误解了 OP 的问题。大概(因为这是来自 JavaFX 控制器的初始化方法,并且他正在调用在TableColumn 类中定义的方法),所有对象都是TableColumn 的实例。所以我将这个问题解释为“我可以在每一列上调用setCellValueFactory(...),并传入一个与列的字段名称相同的参数吗?”。但也许我完全误解了这个问题。
      • @James_D,你是对的。我已经更正了我的答案以包括这种情况。
      猜你喜欢
      • 2015-03-02
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 2014-12-03
      • 1970-01-01
      相关资源
      最近更新 更多