【问题标题】:Is there a way to instantiate a child class with parent object in java?有没有办法在java中用父对象实例化子类?
【发布时间】:2014-07-07 17:29:14
【问题描述】:

我有一个基类说

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 中。

【问题讨论】:

  • 声明一个接受AB构造函数。
  • 您希望得到的B 是什么样子?只需将 A 的字段设置为 null 的 A 实例的副本?
  • 对我来说听起来像是一个 XY 问题...您要解决的真正问题是什么?我无法想象任何像样的用例可以尝试这样做。
  • @Alnitak - 一个不错的用例可能是例如基于通用List 创建一个特定的XXXList。虽然你可能是对的 - 我也很怀疑 OP 的动机:)
  • 如果不显式调用setNamesetAge,就无法设置B 的名称和年龄,因为B 没有采用名称和年龄作为参数。顺便说一句,instanceOfA.getPhone() 是一个编译错误,因为A 没有phonegetPhone

标签: java inheritance instantiation


【解决方案1】:

好的做法是有一个factory 类来“产生”B 的实例。

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

必须编写工厂方法的代码,因为没有基于父类创建子类的标准方法。它总是具体的,取决于你的类的逻辑。

但是,请考虑它是否真的是您需要的。 没有很多智能用例基于其父实例来实例化一个类。一个很好的例子是ArrayList(Collection c) - 构造一个特定 列表(“child”),其中包含generic 集合(“base”)的元素。

实际上,对于许多情况, 有一种模式可以避免这种奇怪的结构。我知道它可能不适用于您的具体情况,因为您写道您的 Base 和 Child 是 3rd 方类。但是您的问题标题足够通用,因此我认为您可能会发现以下内容很有用。

  1. 创建接口IBase
  2. Base类实现接口
  3. 使用composition instead of inheritance - 让Child 使用 Base 而不是继承它
  4. Child实现IBase并将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();
    }
}

在你编辑你的问题之后,我更怀疑你想要的东西是好的。您的问题看起来更像是一种黑客尝试,违反(或不理解)class-based 面向对象概念的原则。在我看来,有人来自 JavaScript 一词并试图保持 JavaScript 编程风格,只是使用不同的 Java 语法,而不是采用不同的语言哲学。

有趣的事实:在prototype-based languages 中可以使用父对象实例化子对象,请参见 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

【讨论】:

  • 这是假设 OP 所需的构造首先是一个好主意...
  • @Alnitak - 你是绝对正确的。我添加了一条注释。
【解决方案2】:

在我看来,你想要避免的方式是非常合适的。某处一定有一段这样的代码。

如果您不能将该方法放在目标类中,请将其放在其他地方(某个工厂)。你应该另外让你的方法static

看看Factory method pattern

第二个选项 将扩展 B 并将此方法作为工厂静态方法放置在该新类中。但是这个解决方案对我来说似乎更复杂。然后你可以打电话给NewB.fromA(A)。然后你应该可以使用你的 NewB 而不是 B。

【讨论】:

    【解决方案3】:

    你可以通过反射来做到这一点:

    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;
                    }
                }
            }
        }
    

    *以上代码复制自在线教程

    【讨论】:

      猜你喜欢
      • 2012-09-16
      • 1970-01-01
      • 2021-06-19
      • 2020-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多