更聪明的方式来编写类似的代码行

时间:2015-12-27 16:35:00

标签: java javafx netbeans-8

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

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

还有其他更聪明的方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:4)

没有特别优雅的方法可以做到这一点,我知道(除了创建很可能对手头任务有点过分的结构),无论你做什么,在某些时候你必须输入属性和将它们与每列相关联。我真的不建议继承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具有类似的功能。)不要忽略这部分,否则当你回到它时你的代码将很难阅读。

如果你真的想用某种循环来做这件事,那么假设你的TableColumn都具有与相应属性相同的名称,你可以使用反射,但我认为你在可读性方面失去了更多你得到的简洁:

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定义的那些。因此,再次假设您的所有TableColumn都具有与属性值匹配的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));
    }
}

答案 1 :(得分:1)

由于这些方法都有相同的共同方法,并且此方法接收相同的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());
  }

}

答案 2 :(得分:1)

第一个问题是,你的变量(date,site_address等)是同一类型的实例吗?

如果是,您可以使用对象和值创建地图,然后对其进行迭代(使用番石榴的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的答案很好,但是这个代码对你的应用程序更加透明(你的类不必实现相同的接口)。