简体   繁体   English

Java,父静态方法返回子类型

[英]Java, parent static method returning child type

I'm new to java and Generics so please bear with me. 我是java和Generics的新手,所以请多多包涵。 I don't even know if this is possible. 我什至不知道这是否可能。 I've looked around and although there seem to be a few posts about this here and there I haven't found one that addresses my specific case clearly enough for me to understand what to do. 我环顾四周,尽管这里似乎有一些相关的帖子,但是我还没有找到一个可以清楚地解决我的具体案例的帖子,我无法理解该怎么做。

I basically have a static method in the parent class and would like it to return various types based on which child calls it. 我基本上在父类中有一个静态方法,并且希望它根据子对象调用它的方式返回各种类型。 The trick here is that said child class that is returned also needs to be instantiated within the method. 这里的技巧是,还需要在方法中实例化返回的子类。

Here's what I mean: 这就是我的意思:

public class Parent {

   public String value;

   public Parent(String value)
   {
      this.value = value;
   }

   public static <T extends Parent> T load(DataStorage data, String key)
   {
      T inst = new ???(data.getValue(key));
      return inst;
   }
}

public class Child extends Parent {
}

Child child = Child.load(dataStore, "key"); // random dataStore instance

I'm not sure where to go from here. 我不确定从这里去哪里。 What do I use in place of ??? 我用什么代替??? which should be whichever child (or Parent ) runs the load() ? 无论哪个孩子(或Parent )运行load() Do I need to also do something along the lines of Child.<Child>load() ? 我是否还需要按照Child.<Child>load()做一些事情?

I'm open to alternative designs if you feel I'm mistaking in trying to do things like this. 如果您觉得自己在尝试做这样的事情时有误会,那么我愿意接受替代设计。 I don't like the idea of having to play around with Reflection in this situation (feels a little hacky) 我不喜欢在这种情况下必须与Reflection一起玩的想法(感觉有些不客气)

Thanks in advance, I really appreciate it. 预先感谢,我非常感谢。

I guess what you want would be possible if Java didn't have type erasure and had 'constructor with parameter constraint' for generic types(like .net, but it has constraint for parameterless constructors only). 我想如果Java没有类型擦除并且对通用类型具有“带参数约束的构造函数”(例如.net,但它仅对无参数构造函数具有约束),您想要的将是可能的。

Maybe those two suits your needs: 也许这两个适合您的需求:

If type of Child based on some selection criteria(eg an enumaration) I would go with a factory pattern like: 如果孩子的类型基于某些选择标准(例如,枚举),我将采用以下工厂模式:

public class ParentFactory {
    Parent create(SomeEnum t, DataStore dataStore, String key) {
        switch (t) {
            case SomeEnum.Child1Related:
                return new Child1(dataStore.get(key));
            ...
        }
    }
}

But if creation is completely irrelevant(which is not in most cases), you can just define an init method in Parent and have initializer code there: 但是,如果创建是完全不相关的(在大多数情况下不是这样),则可以在Parent中定义一个init方法,并在其中具有初始化程序代码:

abstract class Parent {
    String value;
    Parent() {}
    protected void init(String s) { this.value = s; }
    static <T extends Parent> void initialize(DataStore data, String key, T t) {
        t.init(data.getValue(key));
}

Child c = new Child();
Parent.init(dataStore, key, c);

You can make init method as private if you want to prohibit childs to intercept that call. 如果要禁止孩子拦截该调用,可以将init方法设置为私有方法。

Honestly, I favor first one much more. 老实说,我更喜欢第一个。 Second one is a little ugly :) 第二个有点丑陋:)

It sounds like the thing you're looking for is a strongly-typed key object, which can associate a string with the corresponding child type. 听起来您正在寻找的东西是一个强类型的键对象,该对象可以将字符串与相应的子类型相关联。 What I've done in the past is simply write a class for this key type. 我过去所做的只是为此键类型编写一个类。

Assuming your datastore looks something like this: 假设您的数据存储看起来像这样:

public interface DataStore {
  DataItem get(String key);
}

And your various child classes look like this: 您的各种子类如下所示:

public final class Child1 extends Parent {
  public Child1(DataItem dataItem) {
    ...
  }
  ...
}

Your Key type could look like this: 您的Key类型可能如下所示:

/**
 * Represents a way to construct an object from a {@link DataItem}.
 *
 * @param <T> the type of the object to construct.
 */
public final class Key<T extends Parent> {
  private final String key;
  // Assuming Java 8 Function. If you're using Java 7 or older,
  // you can define your own Function interface similarly.
  private final Function<DataItem, T> factoryFunction;

  public Key(String key, Function<String, T> factoryFunction) {
    this.key = checkNotNull(key);
    this.factoryFunction = checkNotNull(factoryFunction);
  }

  public String key() {
    return this.key;
  }

  public T constructFrom(DataItem dataItem) {
    if (!key.equals(dataItem.getKey())) {
      throw new IllegalStateException(
          "DataItem is not valid for key " + key);
    }
    return factoryFunction.apply(dataItem);
  }
}

Then you'll probably want a collection of well-known keys: 然后,您可能需要一些众所周知的键:

/** Well-known {@link Key} instances. */
public final class Keys {
  private Keys() {}  // static class

  /** Key for {@link Child1}. */
  public static final Key<Child1> FIRST_CHILD
      = new Key<>("child1", Child1::new);

  /** Key for {@link Child2}. */
  public static final Key<Child2> SECOND_CHILD
      = new Key<>("child2", Child2::new);

  // etc.
}

Then you can define classes that work with these strongly-typed key instances: 然后,您可以定义适用于这些强类型键实例的类:

public final class Loader {
  private final DataStore dataStore;

  public Loader(DataStore dataStore) {
    this.dataStore = checkNotNull(dataStore);
  }

  public <T extends Parent> T load(Key<T> dataKey) {
    return key.constructFrom(dataStore.get(dataKey.key()));
  }

  ...

}

Note that this example still works even if you don't have Java 8 -- you'll just need to use an anonymous inline class to construct the child, rather than a lambda expression: 请注意,即使您没有Java 8,该示例仍然可以使用-您只需要使用匿名内联类来构造子级,而不是使用lambda表达式:

public static final Key<Child1> FIRST_CHILD =
    new Key<Child1>("child1", new Function<DataItem, Child1>() {
      @Override public Child1 apply(DataItem dataItem) {
        return new Child1(dataItem);
      }
    });

You could of course use reflection for this part if you want, but manually writing the supplier functions will be faster. 当然,您可以根据需要对此部分使用反射,但是手动编写供应商函数会更快。 (Or, if you want the best of both worlds, you could use something like cglib's FastClass .) If you wanted to, you could also make the Key class abstract, so that you would subclass it and override a factory method rather than using a Function . (或者,如果您希望两全其美,则可以使用cglib的FastClass类的FastClass 。)如果需要,还可以将Key类抽象化,以便将其子类化并覆盖工厂方法,而不是使用Function

If you want to, you can merge the Loader type into your Parent class, but I wouldn't, since I think that would violate the Single Responsibility Principle -- typically you want the job of loading domain objects from storage to be separate from the domain objects themselves. 如果愿意,可以将Loader类型合并到您的Parent类中,但我不Parent ,因为我认为这会违反“ 单一职责原则” -通常,您希望从存储区加载域对象的工作与域对象本身。

Hopefully that helps! 希望有帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM