Java接口中引用的对象的新实例

时间:2019-03-28 10:15:22

标签: java polymorphism

我正在从事 JDK6项目

我有一个类似的pojo:

public class MyPojo implements serializable {
  private List<Integer> ids;

  public List<Integers> getIds() {
    return ids;
  }

  public List<Integers> setIds(List<Integer> ids) {
    // idsCopy = copy of ids; // how can I do it?
    this.ids = idsCopy;
  }
}

我想存储在setter中传递的参数ids的副本,但是我不想通过将引用声明为特定实现来专门用于setId方法签名中。 List接口的功能:根据使用pojo的位置,ids可以是LinkedList或ArrayList等。

我想保留ids参数的相同实现。

我该怎么做?

我想到的第一件事是:ids.getClass().newInstance(),但是它必须被InstantiationException和IllegalAccessException的try / catch块包围,尤其是因为我不确定ids的实际实现有一个空的构造函数。还有更直接的东西吗?


更新 在这种情况下,最常见,直接和合理的做法是避免进行复制,而让使用MyPojo类的人传递要设置的对象的副本,例如:

List<Integer> ls = new Arraylist<>();
MyPojo pj = new MyPojo();
pj.setIds(ls.clone()); // or using copy constructor or anything else..

一开始我就有这样的想法:

public <T extends List<Integer> & Cloneable> void setIds(T ids) {
  this.ids = ids.clone();
}

使用也实现了Cloneable的类来强制执行,但Cloneable接口的javadoc很好地解释了为什么它不起作用(以及为什么对克隆的反射也不会如此):

  

一个类实现了Cloneable接口   指示方法   使用该方法制作   该类实例的字段间复制。   

  在未实现该实例的实例上调用Object的clone方法   Cloneable界面导致异常   CloneNotSupportedException被抛出。   

  按照约定,实现此接口的类应重写   使用公共方法的Object.clone(受保护的)。   请参阅有关覆盖此内容的详细信息   方法。   

  请注意,此接口包含clone方法。   因此,不可能仅仅依靠   它实现了此接口的事实。即使调用了clone方法   反思地,并不能保证它会成功。


最后,无论上下文如何,我的问题的答案

  

还有更直接的东西吗?

是“否”,很可能是因为不需要这样做... ..即使..

https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-2384

3 个答案:

答案 0 :(得分:0)

  

还有更直接的东西吗?

有两种可能的方法:

  • 您建议的方法:使用反射调用no-args构造函数以创建一个新的空列表,然后使用List::addAll将元素复制到新列表中。

  • 使用Object::clone方法创建副本。

这两种方法(如果可行)都将创建与原始对象相同类型的列表对象。但是,没有一种方法可以保证对所有可能的List类都适用。

  • 如果列表实现类没有可用/可访问的无参数构造函数,则反射方法将失败。

  • 如果列表实现类未适当覆盖clone,则Object::clone方法将失败。

请注意,这些不是假设的问题。可以故意设计列表实现类,以防止程序克隆列表。例如,列表与数据库查询结果集(例如)之间可能存在某种关系,因此克隆毫无意义。

但是另一方面,如果库设计人员没有提供无参数的构造函数或clone覆盖,则他们可能这样做是有充分的理由。

>

最后,值得怀疑的是,您是否应该尝试完全做到这一点。没有明显的理由让您的MyPojoids属性使用与原始参数相同的List类。这实际上使行为MyPojo类取决于所提供列表的行为。我认为这是一件坏事,因为您正在削弱MyPojo类的抽象边界。

答案 1 :(得分:0)

就像你自己说的那样:

  

我想到的第一件事是:ids.getClass()。newInstance(),但它   对于InstantiationException,需要用try / catch块包围   和IllegalAccessException,尤其是因为我不确定   id的实际实现具有空的构造函数。在那儿   更直接的事情?

当您甚至不知道构造函数的外观时,将如何构造一个基础类的实例?通过反射说,您知道 a (可能不止一个)构造函数的样子,您仍然需要担心以下问题:

  • 私人与内部/打包私人与公共-您如何处理每种情况? List的某些实现(例如在guava库中找到的实现)使用构建器模式来构造集合,因此将构造器设为私有。
  • 构造函数参数的数量-如果不止一个,那么如何构造其余参数?
  • 构造函数引发的异常-如何捕获所有异常?不要说“抓住Exception”,因为您可能会错过Throwable
  • 多个构造函数-您如何确定要调用哪个?
  • 还有更多其他案例需要解决...

您应采用KISS( K t S 简单 S 较旧)的原则,以及SoC( S 分隔 o f C oncerns)原则。保持简单,您将得到回报。让使用Pojo的人决定如何检索列表。只要您(通过文档)明确说明了该方法的作用,就应该由用户对他们如何使用该方法做出合理的决定。

如果他们做类似的事情:

LinkedList<Integer> myIds = ...;
myPojo.setIds(myIds);

然后让他们决定如何检索ids;他们可以转换返回的类型或进行复制。

如果您在文档中明确指出已复制列表,则使用此POJO的人员在检索列表时应知道将其复制为LinkedList,否则应该安全地进行投射到LinkedList的列表。


我仍然会争辩说,即使以上内容也不够简单。最简单的方法(这是被广泛接受的方法)是使用最通用的类​​型进行所需的工作。如果需要映射类型,请使用Map,而不要使用HashMapList/Set元组;如果需要唯一集合,请使用Set,如果需要常规集合,请使用List,等等。

答案 2 :(得分:-1)

例如,您可以使用Arrays.asList

public void setIds(List<Integer> ids) {
    this.ids = Arrays.asList(ids.toArray(new Integer[ids.size()]));
}

这应该适用于Java 1.6