与对象创建相关的字符串
relate toString with object creation
我有一个相当基本的 Java class 和一些 class 变量。我已经覆盖了 toString() 来为我提供字符串输出(最终将输出到文本文件)。
我正在尝试优雅地创建一种方法,让我可以使用此字符串输出来重新创建具有所有变量设置的对象,如前所述。 class 看起来像这样:
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;
}
}
我基本上想将报告中的键(“项目 A 是:”)与相应变量的名称(“itemA”)相关联,并在 toString() 方法和 createReportFromString(字符串字符串)方法。现在,当这样做时,有很多可能的异常可以被抛出,需要处理或抛出 - 然后它看起来没有我想要的那么优雅。
我不知道这是否可以在没有反思的情况下完成 - 或者我可以重新安排这个 class 以使其成为可能?
我无法更改的是 toString() 中字符串输出的结构。
@JimboMcHiggins assuming I can change the toString output how exactly would you tie together serialization and deserialization with some common mapping?
我会保留 toString 不变,并将序列化的责任移至 java.io.Serializable。如果这不是可接受的方法,请纠正我。该映射将由 Report pojo 的 class 字段定义。这还允许您在不破坏现有对象的反序列化的情况下更改您的 toString。
import java.io.Serializable;
public class Report implements Serializable {
private static final long serialVersionUID = 1L;
private String itemA;
private String itemB;
private String 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::");
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 Test1 {
public static void main(String[] args) {
Report report = new Report("W", "O", "W");
System.out.println(report);
String filename = "file.ser";
// Serialization
try
{
//Saving of report in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of report
out.writeObject(report);
out.close();
file.close();
System.out.println("Report has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
Report report1 = null;
// Deserialization
try
{
// Reading the report from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of report
report1 = (Report)in.readObject();
in.close();
file.close();
System.out.println("Report has been deserialized ");
System.out.println(report1);
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
catch(ClassNotFoundException ex)
{
System.out.println("ClassNotFoundException is caught");
}
}
}
输出
Items are::
Item A is: W
Item B is: O
Item C is: W
Report has been serialized
Report has been deserialized
Items are::
Item A is: W
Item B is: O
Item C is: W
反射具有多重特征:
- 在运行时自动发现程序的功能
- 支持处理编译时未知的特性
- 提供程序功能的抽象(例如方法或字段)
您的方法表明您不需要自动发现,因为您明确指定了这三个元素。这是一件好事,因为它使您的程序在未来的更改方面更加健壮,因为处理自动发现的、潜在未知的程序元素会破坏编译器的任何帮助,因为它无法告诉您何时存在不匹配。
您只需要第三点,即对报告元素的抽象。你可以自己创建这样一个抽象,为你的用例量身定制,没有反射,这将更健壮,甚至更高效:
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(…))
。
您还可以删除 setValuesFromString
方法中的 .reset()
调用。当您删除它时,输入字符串中的元素需要与 toString()
方法生成的顺序相同。这会降低它的灵活性,但如果您将代码扩展为包含更多元素,它的效率也会更高。
我有一个相当基本的 Java class 和一些 class 变量。我已经覆盖了 toString() 来为我提供字符串输出(最终将输出到文本文件)。
我正在尝试优雅地创建一种方法,让我可以使用此字符串输出来重新创建具有所有变量设置的对象,如前所述。 class 看起来像这样:
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;
}
}
我基本上想将报告中的键(“项目 A 是:”)与相应变量的名称(“itemA”)相关联,并在 toString() 方法和 createReportFromString(字符串字符串)方法。现在,当这样做时,有很多可能的异常可以被抛出,需要处理或抛出 - 然后它看起来没有我想要的那么优雅。
我不知道这是否可以在没有反思的情况下完成 - 或者我可以重新安排这个 class 以使其成为可能?
我无法更改的是 toString() 中字符串输出的结构。
@JimboMcHiggins assuming I can change the toString output how exactly would you tie together serialization and deserialization with some common mapping?
我会保留 toString 不变,并将序列化的责任移至 java.io.Serializable。如果这不是可接受的方法,请纠正我。该映射将由 Report pojo 的 class 字段定义。这还允许您在不破坏现有对象的反序列化的情况下更改您的 toString。
import java.io.Serializable;
public class Report implements Serializable {
private static final long serialVersionUID = 1L;
private String itemA;
private String itemB;
private String 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::");
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 Test1 {
public static void main(String[] args) {
Report report = new Report("W", "O", "W");
System.out.println(report);
String filename = "file.ser";
// Serialization
try
{
//Saving of report in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of report
out.writeObject(report);
out.close();
file.close();
System.out.println("Report has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
Report report1 = null;
// Deserialization
try
{
// Reading the report from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of report
report1 = (Report)in.readObject();
in.close();
file.close();
System.out.println("Report has been deserialized ");
System.out.println(report1);
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
catch(ClassNotFoundException ex)
{
System.out.println("ClassNotFoundException is caught");
}
}
}
输出
Items are::
Item A is: W
Item B is: O
Item C is: W
Report has been serialized
Report has been deserialized
Items are::
Item A is: W
Item B is: O
Item C is: W
反射具有多重特征:
- 在运行时自动发现程序的功能
- 支持处理编译时未知的特性
- 提供程序功能的抽象(例如方法或字段)
您的方法表明您不需要自动发现,因为您明确指定了这三个元素。这是一件好事,因为它使您的程序在未来的更改方面更加健壮,因为处理自动发现的、潜在未知的程序元素会破坏编译器的任何帮助,因为它无法告诉您何时存在不匹配。
您只需要第三点,即对报告元素的抽象。你可以自己创建这样一个抽象,为你的用例量身定制,没有反射,这将更健壮,甚至更高效:
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(…))
。
您还可以删除 setValuesFromString
方法中的 .reset()
调用。当您删除它时,输入字符串中的元素需要与 toString()
方法生成的顺序相同。这会降低它的灵活性,但如果您将代码扩展为包含更多元素,它的效率也会更高。