【问题标题】:Generalized method to get similar object attributes获取相似对象属性的通用方法
【发布时间】:2015-06-18 13:17:13
【问题描述】:

我有一个对象,它有几个数组作为字段。它的类大致是这样的:

public class Helper {
    InsuranceInvoices[] insuranceInvoices;
    InsuranceCollectiveInvoices[] insuranceCollectiveInvoices
    BankInvoices[] bankInvoices;
    BankCollectiveInvoices[] bankCollectiveInvoices;
}

所有发票类型都有一个相互标记界面发票
我需要获取所有发票才能对其调用另一种方法。

Helper helperObject = new Helper();
// ...

for (InsuranceInvoices invoice : helperObject.getInsuranceInvoices()) {
    Integer customerId = invoice.getCustomerId();
    // ...
}
for (BankInvoices invoice : helperObject.getBankInvoices()) {
    Integer customerId = invoice.getCustomerId();
    // ... 
}

// repeat with all array fields

问题是所有发票只有共同的标记界面。 getCustomerID() 方法不是由相互接口或类定义的。由于给定的规范,这是我无法改变的行为。

for-each-loop 中的代码重复让我很头疼。我必须对四个不同数组中的所有发票对象执行完全相同的操作。因此,有四个 for-each-loop 不必要地使代码膨胀。

有没有办法可以编写通用(私有)方法?一个想法是:

private void generalMethod(Invoice[] invoiceArray){
    // ...
}

但这需要四次 instanceof 检查,因为类 Invoice 不知道方法 getCusomterId()。因此,我将一无所获;该方法仍将包含重复。

我很感谢所有可能的解决方案来概括这个问题!

【问题讨论】:

  • 那么,类都实现了Invoice,也都实现了getCustomerId,但是getCustomerId不在Invoice接口中?您必须使用反射按名称访问方法并调用它。此时,Invoice接口已经相当没用了。
  • getInsuranceInvoicesgetBankInvoices...是否都返回相同数量的发票?
  • 我有一个建议...向将 Invoice 接口设为标记接口而不是指定发票共有哪些方法的人应用提示符。
  • 我没有足够的能力提供完整的答案,但听起来您正在寻找鸭子打字:en.wikipedia.org/wiki/Duck_typing wiki 显示使用动态,并且有 c# 鸭子打字解决方案,但这可能不值得努力
  • @njzk2 没错。反思可能是一种方法,但它通常被认为是一种“肮脏”,不是吗?

标签: java generalization


【解决方案1】:

概括问题的可能解决方案(从最好到最坏排序):

使用包装类

public class InvoiceWrapper {
    private String customerID;
    public String getCustomerID() {
        return customerID;
    }
    public InvoiceWrapper(BankInvoices invoice) {
       this.customerID = invoice.getCustomerID();
    }
    public InvoiceWrapper(InsuranceInvoices invoice) {
       this.customerID = invoice.getCustomerID();
    }
    // other constructors
}

Upd 如果我理解正确,您需要对所有数组中的 ID 做一些事情。要使用 InvoiceWrapper,您还需要在 Helper 类中实现迭代器,它将遍历数组并为每个条目返回一个包装器。因此,无论如何,您将拥有适用于 4 个数组的代码。

使用强制转换实例

public class CustomerIdHelper {
    public static String getID(Invoice invoice) {
        if (invoice instanceof InsuranceInvoices) {
            return ((InsuranceInvoices) invoices).getCustomerID();
        } else if ...
    }
}

通过反射按名称调用方法

public class CustomerIdHelper {
    public static String getID(Invoice invoice) {
        Method method = invoice.getClass().getDeclaredMethod("getCustomerId");
        return (String) method.invoke(invoice);
    }
}

【讨论】:

  • 感谢您的建议!第一种方法是我没有考虑到的。但是你能解释一下为什么你选择反射方法是三者中最差的吗?
  • @DeMo 那是因为如果有人删除了方法,你不会注意到它。在前两个变体中,您会看到编译错误。
【解决方案2】:

它并不漂亮,但是您可以使用反射来查找 getCustomerId Method 然后 invoke() 它,参见。 Class.getDeclaredMethod().

private void generalMethod(Invoice[] invoiceArray){
  try {
    for (Invoice invoice : invoiceArray) {
      Method getCustomerId = invoice.getClass().getDeclaredMethod("getCustomerId");
      getCustomerId.invoke(invoice);
    }
  } catch (Exception e) {
    // ...
  }
}

请注意这是未经测试的。

【讨论】:

    【解决方案3】:

    如果不允许您通过向它们添加自定义接口来更改正在处理的类。您可以做的最好的事情是使用具有所需属性的自定义类包装它们。

    这样,您将拥有一个包含所有“不太好”代码的类,该类将您无法触及的类转换为与适当且有用的设计相匹配的好类。

    例如,您可以拥有一个扩展 WrappedInsurace 并包含成员字段 InsuranceInvoice 的类 WrappedInsuranceInvoice。如果您不需要保留原始课程,则通过复制数据会更好。这样,您可以例如丢失数组并改用列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      • 2021-01-03
      • 2022-11-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多