Java 更新给定超类的子类 问题描述

Java 更新给定超类的子类 问题描述,java,oop,inheritance,Java,Oop,Inheritance,我有一个抽象的Paper类,它包含所有论文的公共属性和一个或多个子类,这些子类为该类型的论文添加了额外的信息。然后我有一个HashMap来存储多篇论文 我的应用程序允许用户通过提供pid,然后提供要更新的属性和值来更新纸张。我遇到的问题是,当我只有一个超类时,如何更新子类上的属性 处理这种情况的最佳方法/实践是什么? 阶级结构 我目前的尝试 这就是我目前拥有的**,它通过使用的实例工作;但我觉得应该有更好的方法来做到这一点 import java.util.*; public class A

我有一个抽象的
Paper
类,它包含所有论文的公共属性和一个或多个子类,这些子类为该类型的论文添加了额外的信息。然后我有一个
HashMap
来存储多篇论文

我的应用程序允许用户通过提供
pid
,然后提供要更新的属性和值来更新纸张。我遇到的问题是,当我只有一个超类时,如何更新子类上的属性

处理这种情况的最佳方法/实践是什么?

阶级结构
我目前的尝试 这就是我目前拥有的**,它通过使用的
实例工作;但我觉得应该有更好的方法来做到这一点

import java.util.*;

public class App {
    public static abstract class Paper {

        private String title;
        private String author;

        public Paper(String title, String author) {
            this.title = title;
            this.author = author;
        }

        public void update(String title, String author) {
            this.title = title;
            this.author = author;
        }
    }


    public static class Publication extends Paper {

        private int pages;

        public Publication(int pages, String title, String author) {
            super(title, author);
            this.pages = pages;
        }

        public void update(String title, String author, int pages) {
            super.update(title, author);
            this.pages = pages;
        }

    }

    public static class PHDThesis extends Paper {

        private String supervisor;

        public PHDThesis(String supervisor, String title, String author) {
            super(title, author);
            this.supervisor = supervisor;
        }

        public void update(String title, String author, String supervisor) {
            super.update(title, author);
            this.supervisor = supervisor;
        }
    }

    public static void main(String[] args) {
        HashMap<String, Paper> papers = new HashMap<String, Paper>();

        papers.put("P001", new PHDThesis("My Super", "My PHD Title", "My Author"));
        papers.put("P002", new Publication(22, "My Pub Title", "My Author"));

        Paper p = papers.get("P001");

        if (p instanceof PHDThesis) {
            ((PHDThesis)p).update("New Title", "New author", "New Super");
        } else if (p instanceof Publication) {
            ((Publication)p).update("New Title", "New author", 33);
        }
    }
}
import java.util.*;
公共类应用程序{
公共静态抽象类论文{
私有字符串标题;
私有字符串作者;
公开论文(字符串标题、字符串作者){
this.title=标题;
this.author=作者;
}
公共无效更新(字符串标题、字符串作者){
this.title=标题;
this.author=作者;
}
}
公共静态类出版物扩展了纸张{
私人网页;
公共出版物(整版页、字符串标题、字符串作者){
超级(标题、作者);
this.pages=页面;
}
公共无效更新(字符串标题、字符串作者、整型页面){
超级更新(标题、作者);
this.pages=页面;
}
}
公共静态类PHDThesis扩展论文{
私人管弦乐监督;
公共PHDThesis(字符串主管、字符串标题、字符串作者){
超级(标题、作者);
this.supervisor=主管;
}
公共无效更新(字符串标题、字符串作者、字符串主管){
超级更新(标题、作者);
this.supervisor=主管;
}
}
公共静态void main(字符串[]args){
HashMap papers=新的HashMap();
论文。put(“P001”,新博士论文(“我的超级”、“我的博士头衔”、“我的作者”);
论文发表(“P002”,新出版物(22,“我的酒吧名称”,“我的作者”);
纸张p=纸张。获取(“P001”);
if(PHDThesis的p实例){
更新(“新标题”、“新作者”、“新超级”);
}else if(发布的p实例){
((出版物)第页)更新(“新标题”,“新作者”,33);
}
}
}
**减少了测试代码,实际代码更加复杂,布局也更好。

创建一个方法

public void update( Map<String, Object> parameters );
公共作废更新(地图参数);
并在文件实现中从中提取相关属性

在出版物中,它可能看起来像:

public void update( Map<String, Object> parameters ) {
  super.update( parameters );

  this.pages = parameters.get( "pages" );
}
public void更新(映射参数){
super.update(参数);
this.pages=parameters.get(“页面”);
}

您可以为每个属性创建一个名为UpdateBundle的对象,其中包含getter

然后Paper类将有一个方法更新(UpdateBundle),每个子类将以不同的方式实现该方法

你所要做的就是为每个孩子调用这个方法,他们就会知道如何处理它

另一方面,我不明白为什么论文类是抽象的。你似乎没有抽象的方法

public abstract class Paper {
    String pid;
    String title;
    String author;

    public void update(PaperUpdateBundle bundle)
    {
        pid = bundle.getPID();
        title = budnle.getTitle();
        author = bundle.getAuthor();
    }
}


public class Publication extends Paper {
    int pages;

    public void update(PaperUpdateBundle bundle)
    {
       super.update(bundle);
       pages = bundle.getPages();
    }
}

public class PHDThesis {
    String supervisor;


    public void update(PaperUpdateBundle bundle)
    {
       super.update(bundle);
       supervisor = bundle.getSupervisor();
    }
}

public interface PaperUpdateBundle
{
    String getPID();
    String getTitle();
    String getAuthor();
    int getPages();
    String getSupervisor();
}

接受答案的问题在于,它要求您手动更新所有属性。如果属性列表发生更改,则必须更改update()方法,否则事情将不同步。根据我的经验,这种情况经常发生。然后你必须花很多时间来追踪这个bug

另一种方法(我不会称之为“更好的”方法)是使用反射或某些第三方库来复制字段。不过,也有一些权衡。这样做的好处是,您的代码所需的工作量要少得多,并且(可能)会有更少的bug。缺点是您的代码速度较慢,灵活性较差,并且缺少编译时检查


我有时会使用Jackson的ObjectMapper.convertValue()来实现这一点。你可以在这里找到其他方法:。

我感觉以前有人问过这个问题,但我找不到答案。如果有任何副本,请链接所有副本。:)不知道为什么会在这里投票否决,发表评论会有所帮助。无论哪种方式,主要的一点是扩展类应该负责理解它的扩展属性,而不是调用类。instanceof的反射破坏了封装。我取消了我的否决票。但是你用原子弹杀死蚊子你的答案没有错,但是它没有对参数进行编译时检查。如果我想传递一个对象(或其他不能编码为字符串的类型)作为参数,它也会中断。但是,,从OOP的角度来看稍微好一点。Paper类在示例中不需要是抽象的,但在实际代码中使用的是抽象方法。我也非常喜欢这个答案。我只想提醒一下——任何额外的参数都需要在扩展对象和UpdateBundle中引入。可能不是问题,但可能会导致维护问题。相反。此要求实际上将强制执行类型安全性,从而减少由替代解决方案(如从地图中提取数据)引入的任何潜在错误。
public abstract class Paper {
    String pid;
    String title;
    String author;

    public void update(PaperUpdateBundle bundle)
    {
        pid = bundle.getPID();
        title = budnle.getTitle();
        author = bundle.getAuthor();
    }
}


public class Publication extends Paper {
    int pages;

    public void update(PaperUpdateBundle bundle)
    {
       super.update(bundle);
       pages = bundle.getPages();
    }
}

public class PHDThesis {
    String supervisor;


    public void update(PaperUpdateBundle bundle)
    {
       super.update(bundle);
       supervisor = bundle.getSupervisor();
    }
}

public interface PaperUpdateBundle
{
    String getPID();
    String getTitle();
    String getAuthor();
    int getPages();
    String getSupervisor();
}