我有一个相当基本的 Java 类,其中包含一些类变量。我已经覆盖 toString() 为我提供了一个字符串输出(最终将输出到一个文本文件)。
我正在尝试优雅地创建一种方法,让我使用此字符串输出来重新创建具有以前设置的所有变量的对象。这个类看起来像这样:
public class Report {
private String itemA;
private String itemB;
private String itemC;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
sb.append("\nItem A is: ").append(itemA);
sb.append("\nItem B is: ").append(itemB);
sb.append("\nItem C is: ").append(itemC);
return sb.toString();
}
}
这就是我可以使用反射来解决它的方法:public class Report {
private String itemA;
private String itemB;
private String itemC;
private final Map<String, String> MAPPING = new HashMap<>();
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
MAPPING.put("Item A is: ", "itemA");
MAPPING.put("Item B is: ", "itemB");
MAPPING.put("Item C is: ", "itemC");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
MAPPING.entrySet().forEach(entry -> {
sb.append("\n").append(entry.getKey()).append(BeanUtils.getProperty(this, entry.getValue()));
});
return sb.toString();
}
public Report createReportFromString(String reportString) {
List<String> reportLines = Arrays.asList(reportString.split("\n"));
HashMap<String, String> stringObjectRelationship = new HashMap<>();
reportLines.forEach(reportLine -> {
Optional<String> matchingKey = MAPPING.keySet().stream().filter(reportLine::contains).findFirst();
matchingKey.ifPresent(key -> {stringObjectRelationship.put(MAPPING.get(key), reportLine.split(key)[1]);});
});
stringObjectRelationship.forEach((variableName, variableValue) -> BeanUtils.setProperty(this, variableName, variableValue));
return this;
}
}
我基本上想将报告中的键(“Item A 是:”)与相应变量的名称(“itemA”)相关联,并在 toString() 方法和 createReportFromString(String string) 方法中使用这种关系。现在,在执行此操作时,可能会抛出许多可能的异常,需要处理或抛出 - 然后它看起来不像我想要的那么优雅。我不知道这是否可以在没有反射的情况下完成 - 或者我可以重新安排这个类(class)以使这成为可能?
我无法改变的是 toString() 中字符串输出的结构。
最佳答案
反射具有多种特征:
您的方法表明您不想要自动发现,因为您明确指定了三个元素。这是一件好事,因为它使您的程序在 future 的更改方面更加健壮,因为处理自动发现的潜在未知程序元素将破坏编译器的任何帮助,因为它无法告诉您何时存在不匹配。
您只需要第三点,即对报告元素的抽象。您可以自己创建这样的抽象,根据您的用例量身定制,无需反射,这将更健壮,甚至更高效:
public class Report {
static final class Element {
final String header;
final Function<Report,String> getter;
final BiConsumer<Report,String> setter;
final Pattern pattern;
Element(String header,
Function<Report, String> getter, BiConsumer<Report, String> setter) {
this.header = header;
this.getter = getter;
this.setter = setter;
pattern = Pattern.compile("^\\Q"+header+"\\E(.*?)$", Pattern.MULTILINE);
}
}
static final List<Element> ELEMENTS = List.of(
new Element("Item A is: ", Report::getItemA, Report::setItemA),
new Element("Item B is: ", Report::getItemB, Report::setItemB),
new Element("Item C is: ", Report::getItemC, Report::setItemC));
private String itemA, itemB, itemC;
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
}
@Override public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are:");
ELEMENTS.forEach(e ->
sb.append('\n').append(e.header).append(e.getter.apply(this)));
return sb.toString();
}
public static Report createReportFromString(String reportString) {
return new Report("", "", "").setValuesFromString(reportString);
}
public Report setValuesFromString(String reportString) {
Matcher m = null;
for(Element e: ELEMENTS) {
if(m == null) m = e.pattern.matcher(reportString);
else m.usePattern(e.pattern).reset();
if(!m.find())
throw new IllegalArgumentException("missing \""+e.header+'"');
e.setter.accept(this, m.group(1));
}
return this;
}
public String getItemA() {
return itemA;
}
public void setItemA(String itemA) {
this.itemA = itemA;
}
public String getItemB() {
return itemB;
}
public void setItemB(String itemB) {
this.itemB = itemB;
}
public String getItemC() {
return itemC;
}
public void setItemC(String itemC) {
this.itemC = itemC;
}
}
这适用于 Java 的开箱即用功能,不需要另一个库来简化操作。请注意,我更改了代码模式,如
createReportFromString
是修改已存在对象的方法的误导性名称。我使用工厂方法的名称来真正创建一个新对象,并添加了另一种设置对象值的方法(作为 toString
的直接对应部分)。如果您仍在使用 Java 8,您可以替换
List.of(…)
与 Arrays.asList(…)
或更好 Collections.unmodifiableList(Arrays.asList(…))
.您也可以删除
.reset()
调用 setValuesFromString
方法。删除它时,输入字符串中的元素必须与toString()
的顺序相同。方法产生。这使得它不太灵活,但如果您扩展代码以包含更多元素,则效率会更高。
关于java - 将 toString 与对象创建相关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65080551/