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

Java中接口引用的对象的新实例,java,polymorphism,Java,Polymorphism,我正在从事一个JDK6项目 我有一个pojo,比如: 公共类MyPojo实现可序列化{ 私有列表ID; 公共列表getid{ 返回ID; } 公共列表setidslistids{ //idsCopy=id的副本;//我怎么做? this.ids=idsCopy; } } 我想存储在setter中传递的参数id的副本,但我不想通过将引用声明为List接口的特定实现来专门化setId方法签名:根据pojo的使用位置,id可以是LinkedList,也可以是ArrayList,等等 我希望保持ids参

我正在从事一个JDK6项目

我有一个pojo,比如:

公共类MyPojo实现可序列化{ 私有列表ID; 公共列表getid{ 返回ID; } 公共列表setidslistids{ //idsCopy=id的副本;//我怎么做? this.ids=idsCopy; } } 我想存储在setter中传递的参数id的副本,但我不想通过将引用声明为List接口的特定实现来专门化setId方法签名:根据pojo的使用位置,id可以是LinkedList,也可以是ArrayList,等等

我希望保持ids参数的相同实现

我怎么复印

我想到的第一件事是:ids.getClass.newInstance,但它需要被InstanceionException和IllegalacessException的try/catch块包围,特别是因为我不确定ids的实际实现是否有空构造函数。还有更直接的事吗

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

List ls=新阵列列表; MyPojo pj=新的MyPojo; pj.setIdsls.clone;//或者使用复制构造函数或其他任何东西。。 一开始我有这样的想法:

公共无效设置IDST ID{ this.ids=ids.clone; } 强制使用一个实现也可克隆的类,但可克隆接口的javadoc很好地解释了为什么这不起作用,以及为什么克隆上的反射不会起作用:

类实现了可克隆的接口 向方法表明它 这种方法是合法的吗 该类实例的逐字段副本。 在未实现克隆的实例上调用对象的克隆方法 可克隆接口导致异常 正在抛出CloneNotSupportedException。 按照约定,实现此接口的类应该重写 Object.clone,它受公共方法保护。 请参阅,以了解有关重写此项的详细信息 方法 请注意,此接口不包含克隆方法。 因此,不可能仅仅凭借 事实上,它实现了这个接口。即使调用了clone方法 反省一下,这并不能保证它会成功

最后,不管上下文如何,我的问题的答案

还有更直接的事吗

不,很可能是因为不需要这样做。。。 ……尽管

您可以使用Arrays.asList进行此操作

public void setIds(List<Integer> ids) {
    this.ids = Arrays.asList(ids.toArray(new Integer[ids.size()]));
}
这应该适用于Java 1.6,您可以使用Arrays.asList,例如

public void setIds(List<Integer> ids) {
    this.ids = Arrays.asList(ids.toArray(new Integer[ids.size()]));
}
这应该适用于Java1.6

还有更直接的事吗

有两种可能的方法可以做到这一点:

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

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

任何一种方法都可以!将创建与原始对象类型相同的列表对象。但这两种方法都不能保证适用于所有可能的列表类

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

如果列表实现类未适当重写Object::clone,克隆方法将失败

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

但另一方面,如果库设计器没有提供无参数构造函数或克隆重写,那么他们这样做可能是有原因的

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

还有更直接的事吗

有两种可能的方法可以做到这一点:

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

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

任何一种方法都可以!将创建与原始对象类型相同的列表对象。但这两种方法都不能保证适用于所有可能的列表类

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

如果列表实现类未适当重写Object::clone,克隆方法将失败

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

但另一方面,如果库设计器没有提供无参数构造函数或克隆重写,那么他们这样做可能是有原因的


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

正如你自己所说:

我想到的第一件事是:ids.getClass.newInstance,但它 实例化异常需要由try/catch块包围 还有IllegalacessException,特别是因为我不确定 ids的实际实现有一个空构造函数。有 更直接的事

当您甚至不知道构造函数是什么样子时,如何构造底层类的实例?通过反思,您知道可能有多个构造函数是什么样子的,您仍然需要担心以下问题:

私有vs内部/包私有vs公共-您如何处理每个案例?列表的一些实现,例如guava库中的实现,使用构建器模式构建集合,因此使构造函数私有。 构造函数参数的数量-如果不止一个,如何构造其余参数? 构造函数引发的异常-如何捕获所有异常?不要说catch Exception,因为您可能会错过Throwable。 多个构造函数-如何决定调用哪个构造函数? 还有更多的角落案件需要处理。。。 你应该采用KISS-Keep-It-Simple-Soldier原则以及SoC关注点分离原则。保持简单,你就会得到回报。让使用Pojo的人决定如何检索列表。只要您通过文档清楚地说明了该方法的作用,用户就可以对如何使用该方法做出合理的决定

如果他们这样做:

LinkedList<Integer> myIds = ...;
myPojo.setIds(myIds);
然后让他们决定如何检索ID;它们可以强制转换返回的类型,也可以制作副本

如果您在文档中明确说明列表是复制的,那么使用此POJO的人在检索列表时应该知道将其复制为LinkedList,否则将列表强制转换为LinkedList应该是安全的


我仍然认为,即使是上述情况也不够简单。最简单的方法是使用最通用的类型来完成所需的工作,这也是被广泛接受的方法。如果需要映射类型,请使用Map,而不是HashMap或列表/元组集;如果需要唯一的集合,请使用集合;如果需要常规集合,请使用列表等。

正如您自己所说:

我想到的第一件事是:ids.getClass.newInstance,但它 实例化异常需要由try/catch块包围 还有IllegalacessException,特别是因为我不确定 ids的实际实现有一个空构造函数。有 更直接的事

当您甚至不知道构造函数是什么样子时,如何构造底层类的实例?通过反思,您知道可能有多个构造函数是什么样子的,您仍然需要担心以下问题:

私有vs内部/包私有vs公共-您如何处理每个案例?列表的一些实现,例如guava库中的实现,使用构建器模式构建集合,因此使构造函数私有。 构造函数参数的数量-如果不止一个,如何构造其余参数? 构造函数引发的异常-如何捕获所有异常?不要说catch Exception,因为您可能会错过Throwable。 多个构造函数-如何决定调用哪个构造函数? 还有更多的角落案件需要处理。。。 你应该采用KISS-Keep-It-Simple-Soldier原则以及SoC关注点分离原则。保持简单,你就会得到回报。让使用Pojo的人决定如何检索列表。只要您通过文档清楚地说明了该方法的作用,用户就可以对如何使用该方法做出合理的决定

如果他们这样做:

LinkedList<Integer> myIds = ...;
myPojo.setIds(myIds);
然后让他们决定如何检索ID;它们可以强制转换返回的类型或 复印一份

如果您在文档中明确说明列表是复制的,那么使用此POJO的人在检索列表时应该知道将其复制为LinkedList,否则将列表强制转换为LinkedList应该是安全的



我仍然认为,即使是上述情况也不够简单。最简单的方法是使用最通用的类型来完成所需的工作,这也是被广泛接受的方法。如果需要映射类型,请使用Map,而不是HashMap或列表/元组集;如果需要唯一的集合,请使用集合;如果需要常规集合,请使用列表等。

好吧。。。OP明确地说,他想保留参数的列表实现类。。。OP明确地说他想保留参数的list实现类。如果你想保留相同的实现,你必须走反射路线。但是真正的问题是:为什么要保持相同的实现?通常,没有必要这样做!直接的方法是克隆。但是列表的一些实现不允许它。有时候,这是不可能的,或者至少是太复杂了,以至于没有同一个实现的副本。你必须坐下来思考为什么你认为你需要这个。您的逻辑是否依赖于实现?是否还有其他事情依赖于获得与在setter中传递的相同类型的返回?作为旁注,但由于id往往是唯一的,我建议使用Set,甚至是SortedSet,如果订单matters@Seelenvirtuose可以使用LinkedList,因为在使用迭代器执行某些操作时,它们关心常量时间,相反,在项目的其他部分,其他人可以使用ArrayList,因为他们需要在固定时间内使用get进行索引访问。我不想强制实现,而且是以隐藏的方式。如果你想保持相同的实现,你必须走反射路线。但是真正的问题是:为什么要保持相同的实现?通常,没有必要这样做!直接的方法是克隆。但是列表的一些实现不允许它。有时候,这是不可能的,或者至少是太复杂了,以至于没有同一个实现的副本。你必须坐下来思考为什么你认为你需要这个。您的逻辑是否依赖于实现?是否还有其他事情依赖于获得与在setter中传递的相同类型的返回?作为旁注,但由于id往往是唯一的,我建议使用Set,甚至是SortedSet,如果订单matters@Seelenvirtuose可以使用LinkedList,因为在使用迭代器执行某些操作时,它们关心常量时间,相反,在项目的其他部分,其他人可以使用ArrayList,因为他们需要在固定时间内使用get进行索引访问。我不想强制实现,而且是以隐藏的方式。MyPojo不会依赖于ids的实现。如果使用列表引用声明ID,那么该接口未公开的所有内容都不会在MyPojo类中使用/需要。此外,一般来说,对我来说,下面这样的行为是不好的,即使我永远不会在项目的任何部分使用isinstance:List ls=new LinkedList;MyPojo pojo=新的MyPojo;pojo.setIdsls;LinkedList的pojo.getIds实例//错误的如果将ID存储在ArrayList for instanceI中,就会发生这种情况。我认为您误解了我的意思。如果将IDS看作MyPojo抽象的一部分,那么列表的实际值的行为应该被视为给定MyPojo的行为的一部分。如果我设置了一个不可变的列表,或者一个具有一些古怪属性的列表呢?好吧,我想我明白你的意思了,如果我错了,请纠正我:如果有一种情况需要LinkedList,而另一种情况是首选ArrayList,然后应该有两个不同的MyPojo类或类似于MyPojo的类,它们引用的id类型是L而不是List。然而,如果一个真正的域对象有这个特殊的需求,我会非常惊讶。如果你真的有这样的需求,更好的方法是让get操作存储给它的列表。如果调用方希望/需要,就让它进行复制。MyPojo将不依赖于ids的实现。如果使用列表引用声明ID,那么该接口未公开的所有内容都不会在MyPojo类中使用/需要。此外,一般来说,对我来说,下面这样的行为是不好的,即使我永远不会在项目的任何部分使用isinstance:List ls=new LinkedList;MyPojo pojo=新的MyPojo;pojo.setIdsls;LinkedList的pojo.getIds实例//错误的如果将ID存储在ArrayList for instanceI中,就会发生这种情况。我认为您误解了我的意思。如果将IDS视为MyPojo抽象的一部分,那么列表的实际值类型的行为应被视为
给定MyPojo的行为。如果我设置了一个不可变的列表,或者一个具有一些古怪属性的列表呢?好吧,我想我明白你的意思了,如果我错了,请纠正我:如果有一种情况需要LinkedList,而另一种情况是首选ArrayList,然后应该有两个不同的MyPojo类或类似于MyPojo的类,它们引用的id类型是L而不是List。然而,如果一个真正的域对象有这个特殊的需求,我会非常惊讶。如果你真的有这样的需求,更好的方法是让get操作存储给它的列表。如果调用者愿意/需要,让调用者制作副本。是的,我同意在这种情况下,不应在内部制作副本,而应让使用setter的人对副本进行所有控制。我同意在这种情况下,不应在内部制作副本,而应让使用setter的人对副本进行所有控制