我是java和Generics的新手,所以请耐心等待。我甚至不知道这是否可行。我环顾四周,虽然在这里和那里似乎有一些关于这个的帖子我没有找到一个能够清楚地解决我的具体案例,让我明白该怎么做。
我基本上在父类中有一个静态方法,并希望它根据哪个子句调用它返回各种类型。这里的技巧是返回的子类也需要在方法中实例化。
这就是我的意思:
test {
dataSource {
url = "jdbc:mysql://localhost/starter_app_test"
driverClassName = "com.mysql.jdbc.Driver"
pooled = true
properties {
...
defaultAutoCommit=false
}
}
}
我不知道从哪里开始。我应该使用什么来代替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
哪个孩子(或???
)运行Parent
?我是否还需要按照load()
?
如果你觉得我错误地尝试做这样的事情,我会接受替代设计。我不喜欢在这种情况下不得不玩反射的想法(感觉有点hacky)
在此先感谢,我真的很感激。
答案 0 :(得分:1)
我想如果Java没有类型擦除并且对于泛型类型具有'带有参数约束的构造函数'(例如.net,但它仅对无参数构造函数有约束),那么你想要的是可能的。
也许这两个适合你的需要:
如果基于某些选择标准(例如,一项侮辱)的儿童类型,我会选择以下工厂模式:
public class ParentFactory {
Parent create(SomeEnum t, DataStore dataStore, String key) {
switch (t) {
case SomeEnum.Child1Related:
return new Child1(dataStore.get(key));
...
}
}
}
但是如果创建完全不相关(在大多数情况下不是这样),你可以在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);
如果你想禁止孩子拦截那个电话,你可以将init方法设为私有。
老实说,我更赞成第一个。第二个有点难看:)
答案 1 :(得分:0)
听起来你正在寻找的是一个强类型的关键对象,它可以将字符串与相应的子类型相关联。我过去所做的只是为这个密钥类型编写一个类。
假设您的数据存储区看起来像这样:
public interface DataStore {
DataItem get(String key);
}
你的各种子课看起来像这样:
public final class Child1 extends Parent {
public Child1(DataItem dataItem) {
...
}
...
}
您的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);
}
}
然后你可能想要一组众所周知的密钥:
/** 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.
}
然后,您可以定义使用这些强类型键实例的类:
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()));
}
...
}
请注意,即使您没有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);
}
});
如果需要,您当然可以对此部分使用反射,但手动编写供应商功能会更快。 (或者,如果你想要两全其美,你可以使用像cglib的FastClass
这样的东西。)如果你想,你也可以将Key
类抽象化,这样你就可以将它子类化了覆盖工厂方法而不是使用Function
。
如果您愿意,可以将Loader
类型合并到Parent
课程中,但我不会,因为我认为这会违反Single Responsibility Principle - 通常您想要从存储中加载域对象的工作与域对象本身是分开的。
希望这有帮助!