2014-10-30 73 views
4

假设我们有以下几点:Java列表<Parent>和列表<Children>方法

public class Parent{ 
    public void setXXX(String XXX); 
    public String getXXX(); 
} 

public class Children extends Parent{ 
.... 
} 

现在我想创建一个像下面这样的方法称为克隆列表:

public List<Something> cloneList(List<Something> original){ 
    List<Something> newList=new ArrayList<>(); 
    for(Something e:original){ 
     Something newE=new Something(); 
     newE.setXXX(e.getXXX()); 
     newList.add(newE); 
    } 
    return newList; 
} 

的事情是我们要cloneList可以应用于List<Parent>List<Children>,所以无论如何适用于“某事”?

东西不能“扩展父?”或“父”,由于Java的Collection<Parent>Collection<Children>

假设不兼容: 1.不要想用任何方式序列化或反射。

  1. 我们无法修改父类和子类。这是在第三方Jar中预定义的。

  2. SuperParent类是不可能的,因为在2

+1

考虑一个事实,即您不能在您的帖子中发布< >括号除非你将其格式化为代码。 – 2014-10-30 15:10:54

+0

使用泛型... – brso05 2014-10-30 15:15:10

+1

没有解决方案满足您添加的所有约束。要创建其类不是静态已知的对象的忠实副本,您需要使用静态已知可用的反射或方法。如果这些类已经有可用的复制方法,那么你肯定会在你的示例代码中使用它。 (请注意''Object.clone()'可以达到这个目的,但是只有当类'Parent'实现'Cloneable'时,即使这样做,Object.clone()可能会很成问题。) – 2014-10-30 16:14:03

回答

0

变化cloneList到签名声明,我们不能修改父:

public <X extends Parent> List<X> cloneList(final List<X> original) 

然后它会工作,至少在方法签名。您可以在内部构建一个List<Parent>,然后将其转换为List<X>,并在需要时忽略警告;没有办法找出“X”的运行时类型。

1

你不能使用泛型这种方式,只能反射。

对于类型变量T,不能使用new T()。这是因为泛型是编译时机制,并且在运行时使用new来创建特定类型的对象,并且编译器无法在编译时创建对该类型的适当引用。因此,尽管这样的:

new ArrayList<T>(); 

是合法的,因为编译器实际上编译成的代码创建的原始ArrayList类型,这样的:

new T(); 

不是,因为编译器甚至不知道是什么实际的类将是(即使它只是被定义为T extends Parents它可能是一个甚至没有编写程序时编写的类,如Grandchildren或其他),甚至不知道它是否具有无参数构造函数。

2

这在Java中是不可能的。看看Generic syntax for extends or equal to

你可以改变你的方法如下,让你的父类扩展SuperParent。

public static <T extends SuperParent> List<T> cloneList(List<T> original, Class<T> type) throws IllegalAccessException, InstantiationException { 
    List<T> newList=new ArrayList<>(); 
    for(T e : original){ 
     T x = type.newInstance(); 
     x.setXXX(e.getXXX()); 
     newList.add(x); 
    } 
    return newList; 
} 

此外,您可以选择其他克隆方法。例如,使用Apache的百科全书SerializationUtils

List<Children> result = (List<Children>) SerializationUtils.clone(originalList); 
+1

同意更多关于序列化的建议(或者可怕的'克隆',但忘记我刚刚说过的),而不是基于反思的建议。首先,它假设存在一个无参数的构造函数。其次,新副本虽然具有正确的类型,但不具备原件的完整内部状态,这就是为什么序列化更受欢迎的原因。 – RealSkeptic 2014-10-30 15:47:29

0

在一般意义上,你应该能够使用具有这种签名的方法:

public <T extends Parent> List<T> cloneList(List<T> original) 

这不是你最大的问题,但是。这将获得列表元素的副本。您的代码不能使用

T newE = new T(); // doesn't work 

因为类型参数T的空构造函数的存在不能保证。相反,您需要一种能够返回正确类型副本的方法。你不能完全安全地做到这一点,但你可以靠近。您可以实现这些方法:

public Parent Parent.copy(); 
public Children Children.copy(); 

...以任何方式是适当的,然后写出像这样的方法:

public <T extends Parent> List<T> cloneList(List<T> original) { 
    List<T> newList = new ArrayList<>(); 

    for (T originalItem : original) { 
     newList.add(original.getClass().cast(original.copy())); 
    } 

    return newList; 
} 

(请注意,虽然Object.getClass()的记录返回类型为Class<?>,这对于这个目的不起作用,method documentation表示返回类型实际上比这个更具体一些,足以使这项工作成功。)

+0

'new T();'不能仅仅因为空构造函数而被使用,而且因为你不能实例化一个类型变量。但更重要的是,如果类中有任何最终字段,则'copy()'方法可能无法正确地复制内部状态。复制构造函数将是需要的,然后您需要使用'getConstructor(...)'。 – RealSkeptic 2014-10-30 16:17:03

+0

@RealSkeptic我提出的'copy()'方法不是将状态从一个对象复制到另一个对象,而是返回调用该对象的副本。它可以做任何它需要做的事情,包括调用一个拷贝构造函数。它不需要使用反射,因为它是对象类的实例方法,因此它知道要使用什么构造函数以及如何实例化该类的对象。 – 2014-10-30 17:15:04