【问题标题】:Use actual type of the class when using inheritance使用继承时使用类的实际类型
【发布时间】:2015-02-04 20:03:49
【问题描述】:

假设我们有一个具有以下方法的类:

public class Entry {
    private String name;

    public static Entry getOrCreate(String name) {
        // ...
        return new Entry(name);
    }
}

这个类可能是子类(例如SubEntry),并且“getOrCreate”背后的逻辑不会改变。但子类不应返回 Entry 类型的新对象,而是相应子类的类型(例如 return SubEntry(name)

如果不对Entry 的每个子类重新实现方法getOrCreate,我如何才能实现这一点?这种技术有术语吗?

【问题讨论】:

  • 我认为泛型会让你到达那里。见docs.oracle.com/javase/tutorial/java/generics/types.html
  • @ochi,更准确地说是泛型 + 反射,因为如果使用泛型,他将需要反射来创建实例
  • @AndyBrown 够公平的!我只是不确定静态方法的要求有多严格。另外,您可以随时返回Object 对吗?
  • 这可能会有所帮助:stackoverflow.com/questions/14353620/… 但实际上将静态方法添加到每个子类会更容易。如果确实有很多重复的代码,您可以将其分解为基类中的辅助函数。
  • 您希望如何创建SubEntry 对象,您要调用:Entry.getOrCreate(name),或SubEntry.getOrCreate(name),或new SubEntry(name)

标签: java inheritance types casting


【解决方案1】:

子类化Entry 不会影响getOrCreate 方法,因为静态方法不是类实例的一部分;它们在逻辑上不属于任何类别。

如果您改为将getOrCreate 移动到非静态工厂类中,您可以使用一些泛型魔法来确定返回的类型:

public class Entry {
    private String name;
}

abstract class AbstractEntryFactory<T extends Entry>
    public abstract T getOrCreate(String name);
}

public class EntryFactory extends AbstractEntryFactory<Entry>
    @Override
    public Entry getOrCreate(String name) {
        // ...
        return new Entry(name);
    }
}

public class SubEntryFactory extends AbstractEntryFactory<SubEntry>
    @Override
    public SubEntry getOrCreate(String name) {
        // ...
        return new SubEntry(name);
    }
}

实际上调用getOrCreate 看起来与您的代码看起来不同。而不是这个:

Entry myEntry = Entry.getOrCreate("my name");

它看起来像这样:

Entry myEntry = new EntryFactory().getOrCreate("my name");

或者这个:

SubEntry myEntry = new SubEntryFactory().getOrCreate("my name");

【讨论】:

    【解决方案2】:

    假设您希望能够调用Entry.getOrCreate() 来创建一种SubEntry,您必须传递一些额外的信息。原因是getOrCreate() 方法没有被SubEntry 继承,因为它是static 方法。因此,如果您想按照我提到的方式调用它,则必须传递要创建的类名。在下面的代码中,没有检查来验证 Class clazzEntry 还是子类型,但这为您提供了一个开始。

    import java.lang.reflect.Constructor;
    
    public class TestClass {
        public static void main(String[] args) {
            Entry entry = (Entry)Entry.getOrCreate("entry", Entry.class);
            SubEntry subEntry = (SubEntry)SubEntry.getOrCreate("subEntry", SubEntry.class);
    
            System.out.println("entry class: " + entry.getClass().getName());
            System.out.println("subEntry class: " + subEntry.getClass().getName());
        }
    }
    
    class Entry {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public static Object getOrCreate(String name, Class clazz) {
            // If a constructor is created that takes a String, such as "public Entry(String name)",
            // then each sub class will need to implement that method. Instead I used a getter and
            // setter for the name attribute.
            try {
                Entry entry = (Entry)clazz.newInstance();
                entry.setName(name);
                return entry;
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    class SubEntry extends Entry {
    }
    

    最终结果是这样的输出:

    entry class: Entry
    subEntry class: SubEntry
    

    【讨论】:

      【解决方案3】:

      您要问两个问题:

      1. 我该怎么做?
      2. 这种技术叫什么?

      第二个比第一个重要得多。

      在我看来,您想要实现的目标类似于克隆 (link) 或虚拟构造函数 的概念。但是您希望这是一个静态方法,这提出了为什么?由于静态方法与某个类而非实例相关联,因此您应该通过该类调用它,在这种情况下,您也可以显式调用new。但是在搜索“静态上下文中的检索类”之后,我会说不可能完全按照你在问题中写的那样做。

      如果将静态方法转换为普通方法,可以使用反射来完成:

      class Entry {
          private String name;
          public Entry(String name) {
              this.name = name;
          }
          public Entry() {
              this.name = null;
          }
          public Entry getOrCreate(String name) {
              try {
                  return getClass().getConstructor(String.class).newInstance(name);
              } catch (Exception e) {
                  return new Entry(name);
              }
          }
      }
      
      class BetterEntry extends Entry {
          public BetterEntry(String name) {
              super(name);
          }
          public BetterEntry() {
              super();
          }
      }
      

      然后您将从实例中调用该函数,如下所示:

      Entry a = new Entry().getOrCreate("First");
      Entry b = new BetterEntry().getOrCreate("Second");
      Entry c = b.getOrCreate("Third");
      

      a、b、c的动态类型分别为Entry、BetterEntry和BetterEntry。您可以省略默认构造函数,但我添加了它们以使调用 getOrCreate 感觉更像是一个静态方法。

      如果你真的希望方法是静态的,最简单的方法就是在每个子类中重新实现这个函数。

      【讨论】:

        猜你喜欢
        • 2015-08-22
        • 1970-01-01
        • 1970-01-01
        • 2016-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-05
        相关资源
        最近更新 更多