我有一个相当基本的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 is:”)与相应变量(“ itemA”)的名称相关联,并在toString()方法和createReportFromString(String string)方法中使用此关系。现在,当执行此操作时,可能会抛出很多可能的异常,并且需要对其进行处理或抛出-然后,它看起来比我想要的优雅得多。
我不知道是否可以在没有反思的情况下进行操作-也许我可以重新安排此类以使其成为可能?
我无法更改的是toString()中输出的字符串的结构。
反射具有多个功能:
你的方法建议你不要自动发现,因为你要明确指定这三个元素。这是一件好事,因为它会使你的程序在将来的更改方面更强大,因为处理自动发现的,潜在未知的程序元素将破坏编译器的任何帮助,因为它无法告诉你何时存在不匹配情况。
你只需要第三点,即对报告元素的抽象。你可以自己创建适合你的用例的抽象,而无需使用Reflection,这样的抽象将更加健壮,甚至更加高效:
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()
方法产生的顺序相同。这使它的灵活性降低了,但是如果将代码扩展为包含更多元素,效率也会更高。
我是一个相当新的开发人员,但是这里有很多地方可供我选择,也有很多需要学习的地方。感谢您抽出宝贵的时间对此进行解释。