Java 将字符串与对象创建关联

Java 将字符串与对象创建关联,java,object,reflection,Java,Object,Reflection,我有一个相当基本的Java类和一些类变量。我已经过度使用toString()来提供字符串输出(最终将输出到文本文件) 我试图优雅地创建一种方法,让我使用这个字符串输出重新创建对象,并像以前一样设置所有变量。这个类看起来像这样: public class Report { private String itemA; private String itemB; private String itemC; @Override public String to

我有一个相当基本的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;
    }
}
公共类报告{
私有字符串项a;
私有字符串项b;
私有字符串项c;
私有最终映射=新HashMap();
公共报告(字符串项A、字符串项B、字符串项C){
this.itemA=itemA;
this.itemB=itemB;
this.itemC=itemC;
MAPPING.put(“项目A为:”,“项目A”);
put(“B项为:”,“B项”);
put(“C项为:”,“C项”);
}
@凌驾
公共字符串toString(){
StringBuilder sb=新的StringBuilder();
sb.追加(“项目为::”);
MAPPING.entrySet().forEach(条目->{
sb.append(“\n”).append(entry.getKey()).append(BeanUtils.getProperty(this,entry.getValue());
});
使某人返回字符串();
}
公共报表createReportFromString(String reportString){
List reportLines=Arrays.asList(reportString.split(“\n”);
HashMap stringObjectRelationship=新建HashMap();
reportLines.forEach(reportLine->{
可选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));
归还这个;
}
}
我基本上希望将报表中的键(“项A是:”)与相应变量的名称(“项A”)关联起来,并在toString()方法和createReportFromString(String String)方法中使用此关系。现在,当执行此操作时,有很多可能的异常可以抛出,需要处理或抛出-然后它看起来没有我想要的那么优雅

我不知道这是否可以在没有反射的情况下实现——或者我可以重新安排这个类使之成为可能

我不能更改的是toString()中字符串输出的结构


@JimboMcHiggins假设我可以更改toString输出,那么您如何将序列化和反序列化与一些常见的映射联系起来

我将保持toString不变,并将序列化的责任转移到java.io.Serializable。如果这不是一个可接受的方法,请纠正我。映射将由报表pojo的类字段定义。这还允许您在不中断现有对象的反序列化的情况下更改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

反射具有多个特征:

  • 在运行时自动发现程序的功能
  • 支持处理编译时未知的特性
  • 提供程序功能的抽象(例如方法或字段)
  • 您的方法表明您不希望自动发现,因为您明确指定了这三个元素。这是一件好事,因为它使您的程序对于未来的更改更加健壮,因为处理自动发现的、潜在未知的程序元素将破坏编译器的任何帮助,因为它无法告诉您何时存在不匹配

    您只需要第三点,即对报告元素的抽象。您可以自己创建这样一个抽象,根据您的用例进行定制,而无需反射,这将更加健壮,甚至更加高效:

    公共类报告{
    静态最终类元素{
    最终字符串标题;
    最终功能吸气剂;
    最终双消费者设定者;
    最终模式;
    元素(字符串头,
    函数getter、双消费者setter){
    this.header=头;
    this.getter=getter;
    this.setter=setter;
    pattern=pattern.compile(“^\\Q”+头+”\\E(.*?$”,pattern.MULTILINE);
    }
    }
    静态最终列表元素=List.of(
    新元素(“项目A为:”,报告::getItemA,报告::setItemA),
    新元素(“项B为:”,报告::getItemB,报告::setItemB),
    新元素(“项C为:”,报告::getItemC,报告::setItemC));
    私有字符串itemA、itemB、itemC;
    公共报告(字符串项A、字符串项B、字符串项C){
    this.itemA=itemA;
    this.itemB=itemB;
    this.itemC=itemC;
    }
    @重写公共字符串toString(){
    StringBuilder sb=新的StringBuilder();
    sb.追加(“项目为:”);
    元素。forEach(e->
    sb.append('\n').append(e.header).append(e.getter.apply(this));
    使某人返回字符串();
    }
    公共静态报表createReportFromString(String reportString){
    返回新报表(“,”“,”).setValuesFromString(reportString);
    }
    公共报告集合值fromstring(String reportString){
    匹配器m=null;
    对于(元素e:元素){
    如果(m==null)m=e.pattern.matcher(reportString);
    else m.usePattern(e.pattern).reset();
    如果(!m.find())
    抛出新的IllegalArgumentException(“缺少\”“+e.header+”);
    e、 setter.accept(这个,m.group(1));
    }
    归还这个;
    }
    公共字符串getItemA(){
    返回项目a;
    }
    公共void setItemA(字符串itemA){
    this.itemA=itemA;
    }
    公共字符串getItemB(){
    返回项目B;
    }
    公共void setItemB(字符串itemB){
    this.itemB=itemB;
    }
    公共字符串getItemC(){
    返回项目c;
    }