使用继承时使用 class 的实际类型

Use actual type of the class when using inheritance

假设我们有一个 class,方法如下:

public class Entry {
    private String name;

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

这个class可以被subclassed(例如SubEntry),"getOrCreate"背后的逻辑没有改变。但是 subclasses 不应该 return 类型 Entry 的新对象,而是相应 subclass 类型的新对象(例如 return SubEntry(name)

如何在不为 Entry 的每个子 class 重新实现方法 getOrCreate 的情况下实现这一点?这种技术有专门的术语吗?

Subclassing Entry 不会影响 getOrCreate 方法,因为静态方法不是 class 实例的一部分;他们在逻辑上不属于任何 class.

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

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");

假设您希望能够调用 Entry.getOrCreate() 来创建一种 SubEntry,您将必须传递一些额外的信息。原因是 getOrCreate() 方法没有被 SubEntry 继承,因为它是 static 方法。因此,如果您想按照我提到的方式调用它,则必须传递要创建的 class 名称。在下面的代码中,没有检查来验证 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

您有两个问题要问:

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

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

在我看来,您想要实现的目标类似于克隆 (link) 或虚拟构造函数。但是您希望这是一个静态方法,这就提出了一个问题,为什么?由于静态方法绑定到某个 class,而不是实例,因此您应该通过 class 调用它,在这种情况下您也可以显式调用 new。但是搜索 "retrive class in static context" 我会说不可能完全按照你在问题中写的那样做。

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

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 感觉更像是一个静态方法。

如果您真的希望该方法是静态的,最简单的方法就是在每个子class.

中重新实现该函数