在Java中有没有一种方法使用父类对象实例化子类?

7

I have a base class say

class A {
   private String name;
   private String age;

   //setters and getters for same
}

还有一个子类称为

class B extends A {
 private String phone;
 private String address;

  //setters and getters for same
}

现在我有一个A类的实例,除此之外还需要设置B类中的字段,所以代码应该是这样的:

A instanceOfA = gotAFromSomewhere();
B instanceOfB = constructBFrom(instanceOfA);
instanceOfB.setPhone(getPhoneFromSomewhere());
instanceOfB.setAddress(getAddressFromSomewhere());

我可以使用给定的A来实例化B,但我不想这样做

B constructBFrom(A instanceOfA) {
  final B instanceOfB = new B();
  instanceOfB.setName(instanceOfA.getName());
  instanceOfB.setPhone(instanceOfA.getAge());

  return B;
}

我希望您能提供一个通用的功能来构建对象,例如:

public class SomeUtility {

   public static <T1, T2> T2 constructFrom(T1 instanceOfT1, Class<T2> className) {

      T2 instatnceOfT2 = null;

      try {
         instatnceOfT2 = className.newInstance();
         /*
         * Identifies the fields in instanceOfT1 which has same name in T2
         * and sets only these fields and leaves the other fields as it is.
         */
      } catch (InstantiationException | IllegalAccessException e) {
         // handle exception
      }          

      return instatnceOfT2;
   }
}

这样我就可以用它作为

B constructBFrom(A instanceOfA) {
   return SomeUtility.constructFrom(instanceOfA, B.class);
}

此外,用例不仅限于父子类,而且此实用函数可以用于适配器用例。
PS- A和B是第三方类,我只能使用这些类,因此无法对A和B进行任何修改。

声明一个接受 A 的构造函数 B - Sotirios Delimanolis
2
对我来说,这听起来像是一个“XY问题”...你真正想要解决的问题是什么?我无法想象尝试做这件事情有任何合理的用例。 - Alnitak
@Alnitak - 一个不错的应用场景可能是基于通用的List创建一个特定的XXXList。尽管你可能是对的 - 我也对楼主的动机持怀疑态度 :) - Honza Zidek
无论如何,如果没有显式调用setNamesetAge,则无法设置B的名称和年龄,因为如所写,B没有以名称和年龄作为参数的构造函数。顺便说一下,instanceOfA.getPhone()是一个编译错误,因为A没有phonegetPhone - David Conrad
@shelly - 请看我下面回答的修改部分。告诉我们你想要这个黑客干什么。从头到尾看起来都很不正当。 - Honza Zidek
显示剩余2条评论
3个回答

12

最佳实践是拥有一个factory类,该类“生成”B的实例。

public class BFactory {
    public B createBFromA(A a) { ... }
}

您需要编写工厂方法的代码,因为没有标准的方法可以根据父类创建子类。它始终是具体的,并取决于类的逻辑。
然而,请考虑是否真的需要这样做。实例化基于其父类的实例的类的用例并不多。一个很好的例子是ArrayList(Collection c)-构造一个包含“基本”元素的“特定”列表(“子级”)的特定集合。
实际上,在许多情况下,有一种模式可以避免这种奇怪的构造方法。我知道这可能不适用于您的特定情况,因为您写道您的Base和Child是第三方类。但是,您的问题标题足够通用,我认为您可能会发现以下内容有用。
以下是翻译的内容:
  1. 创建一个名为 IBase 的接口
  2. 让类 Base 实现该接口
  3. 使用组合而非继承 - 让 Child 使用 Base 而不是继承它
  4. Child 实现 IBase 接口,并将所有方法委托给 Base 实例

你的代码应该如下所示:

public interface IBase {
    String getName();
    int getAge();
}

public class Base implements IBase {
    private String name;
    private int age;
    // getters implementing IBase
}

public class Child implements IBase {
    // composition:
    final private IBase base;        
    public Child(IBase base) {
        this.base = base;
    }
    // delegation:
    public String getName() {
        return base.getName();
    }
    public int getAge() {
        return base.getAge();
    }
}

在你编辑了问题之后,我更加怀疑你想要的是否正确。你的问题看起来更像是企图违反(或者不理解)基于类面向对象概念的原则的黑客行为。对我而言,这听起来像是来自JavaScript世界的人,试图保持JavaScript编程风格,只是使用Java的不同语法,而不是采用不同的语言哲学。

有趣的事实:在基于原型的语言中,使用父对象实例化子对象是可能的,在JavaScript 1.8.5中有一个示例:

var base = {one: 1, two: 2};
var child = Object.create(base);
child.three = 3;

child.one;   // 1
child.two;   // 2
child.three; // 3

3
假设原始问题中所需的结构本身就是一个好主意…… - Alnitak
@Alnitak - 你说得完全正确。我已经添加了一条注释。 - Honza Zidek

2
在我看来,你想要避免的方式非常恰当。一定有一个这样的代码片段存在。
如果你无法将该方法放入目标类中,请将其放在其他地方(例如工厂)。此外,你应该将该方法设置为static
请参考工厂方法模式
第二种选择是扩展B并将此方法作为工厂静态方法放置在新类中。但对我来说,这个解决方案似乎更加复杂。然后你可以调用NewB.fromA(A)。然后你应该能够使用NewB代替B。

0
你可以通过反射来实现:
public static void copyFields(Object source, Object target) {
        Field[] fieldsSource = source.getClass().getFields();
        Field[] fieldsTarget = target.getClass().getFields();

        for (Field fieldTarget : fieldsTarget)
        {
            for (Field fieldSource : fieldsSource)
            {
                if (fieldTarget.getName().equals(fieldSource.getName()))
                {
                    try
                    {
                        fieldTarget.set(target, fieldSource.get(source));
                    }
                    catch (SecurityException e)
                    {
                    }
                    catch (IllegalArgumentException e)
                    {
                    }
                    catch (IllegalAccessException e)
                    {
                    }
                    break;
                }
            }
        }
    }

*从在线教程复制的代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接