我如何概括这一点?
How do I generalize this?
我有一个 class ExampleProperty
扩展了 superclass Property
和一个单独的 class, GameObject
, 我有ArrayList<Property> properties
以及以下方法,其中 Engine.createEngineObject(new ExampleProperty())
只是将 new ExampleProperty()
添加到列表中,然后 return 将其返回,propertyId
是 [的继承成员=13=],其中 properties
是 Property
的列表:
public ExampleProperty addExampleProperty()
{
ExampleProperty temp = (ExampleProperty)Engine.createEngineObject(new ExampleProperty());
temp.propertyId = properties.size();
return temp;
}
我想推广这个方法,这样我就可以传入一个 Class<?> propertyClass
并让它 return 一个与 propertyClass
类型相同的对象(并在第 3 行执行转换?)。恐怕我对泛化还不够熟悉,所以感谢任何帮助:)
编辑:这是发布答案后修改后的代码。我忘了提及一些信息,但我已经完成了,所以这就是我所拥有的。这可能很糟糕,但它是我的,它有效,我为此感到自豪:
public <T extends Property> T addProperty(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
if(propertyClass.getSuperclass() == Engine.class)
{
Engine temp = Engine.createEngineObject(propertyClass.newInstance());
temp.propertyId = properties.size();
return (T)temp;
}
T temp = propertyClass.newInstance();
temp.propertyId = properties.size();
return (T)temp;
}
这可能会成功:
public <T extends Property> T createEngineObject(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
// do something...
return property;
}
不过,传递 Property
实例似乎是个更好的主意:
public <T extends Property> T createEngineObject(T property) {
// do something...
return property;
}
这里的中心问题是您对 ExampleProperty
所做的事情没有在类型中捕获。因此,您不能一字不差地一概而论。
让我们用 currently-hardcoded 类型 (ExampleProperty) 列出您 'do' 的内容,以便我们知道要在类型中捕获什么,以便我们可以概括它:
- 你调用它的no-args构造函数
- 您将其结果作为第一个参数传递给
Engine.createEngineObject
。
- 您将
createEngineObject
调用的结果转换为这种类型。
- 您引用了该类型的
propertyId
字段。
- 你return类型。
在所有这些操作中,到目前为止问题最大的是第一个:构造函数不参与类型系统。您不能在 java 类型中声明 'has a no-args constructor'。完全没有。随便。
问题有两种解决方法:
- 将其仅在零编译时间保证的文档中声明,并使用
java.lang.Class
实例作为载体,以便代码知道从何处获取该构造函数。将不会 compile-time 检查您传递给此方法的泛型形式的任何类型实际上是否具有 no-args 构造函数,或者它是 non-abstract。如果您未能满足这些条件,您唯一知道的方式就是在运行时会发生一些异常。
- 使用工厂。
正确答案当然是第二个:使用工厂。捕捉 'a thing that can make ExampleProperty instances' 的概念,因为 可以 在 java 的类型系统中传达。我们可以使用这个工厂作为任何适用于整个类型的工具,如果出现的话:
您的通用代码:
public interface PropertyFactory<P extends Property> {
P make();
}
// NB: You can replace the above with `java.util.function.Supplier` instead, but I'd make your own type.
public abstract class Property {
int propertyId;
}
public <P extends Property> P addProperty(PropertyFactory<P> factory) {
P temp = Engine.createEngineObject(factory.make());
temp.propertyId = properties.size();
return temp;
}
// in Engine.java
public <P extends Property> P createEngineObject(P property) {
// do stuff here
return property;
}
然后使用:
private static final PropertyFactory<ExampleProperty> EXAMPLE_MAKER = ExampleProperty::new;
ExampleProperty ep = addProperty(EXAMPLE_MAKER);
我有一个 class ExampleProperty
扩展了 superclass Property
和一个单独的 class, GameObject
, 我有ArrayList<Property> properties
以及以下方法,其中 Engine.createEngineObject(new ExampleProperty())
只是将 new ExampleProperty()
添加到列表中,然后 return 将其返回,propertyId
是 [的继承成员=13=],其中 properties
是 Property
的列表:
public ExampleProperty addExampleProperty()
{
ExampleProperty temp = (ExampleProperty)Engine.createEngineObject(new ExampleProperty());
temp.propertyId = properties.size();
return temp;
}
我想推广这个方法,这样我就可以传入一个 Class<?> propertyClass
并让它 return 一个与 propertyClass
类型相同的对象(并在第 3 行执行转换?)。恐怕我对泛化还不够熟悉,所以感谢任何帮助:)
编辑:这是发布答案后修改后的代码。我忘了提及一些信息,但我已经完成了,所以这就是我所拥有的。这可能很糟糕,但它是我的,它有效,我为此感到自豪:
public <T extends Property> T addProperty(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
if(propertyClass.getSuperclass() == Engine.class)
{
Engine temp = Engine.createEngineObject(propertyClass.newInstance());
temp.propertyId = properties.size();
return (T)temp;
}
T temp = propertyClass.newInstance();
temp.propertyId = properties.size();
return (T)temp;
}
这可能会成功:
public <T extends Property> T createEngineObject(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
// do something...
return property;
}
不过,传递 Property
实例似乎是个更好的主意:
public <T extends Property> T createEngineObject(T property) {
// do something...
return property;
}
这里的中心问题是您对 ExampleProperty
所做的事情没有在类型中捕获。因此,您不能一字不差地一概而论。
让我们用 currently-hardcoded 类型 (ExampleProperty) 列出您 'do' 的内容,以便我们知道要在类型中捕获什么,以便我们可以概括它:
- 你调用它的no-args构造函数
- 您将其结果作为第一个参数传递给
Engine.createEngineObject
。 - 您将
createEngineObject
调用的结果转换为这种类型。 - 您引用了该类型的
propertyId
字段。 - 你return类型。
在所有这些操作中,到目前为止问题最大的是第一个:构造函数不参与类型系统。您不能在 java 类型中声明 'has a no-args constructor'。完全没有。随便。
问题有两种解决方法:
- 将其仅在零编译时间保证的文档中声明,并使用
java.lang.Class
实例作为载体,以便代码知道从何处获取该构造函数。将不会 compile-time 检查您传递给此方法的泛型形式的任何类型实际上是否具有 no-args 构造函数,或者它是 non-abstract。如果您未能满足这些条件,您唯一知道的方式就是在运行时会发生一些异常。 - 使用工厂。
正确答案当然是第二个:使用工厂。捕捉 'a thing that can make ExampleProperty instances' 的概念,因为 可以 在 java 的类型系统中传达。我们可以使用这个工厂作为任何适用于整个类型的工具,如果出现的话:
您的通用代码:
public interface PropertyFactory<P extends Property> {
P make();
}
// NB: You can replace the above with `java.util.function.Supplier` instead, but I'd make your own type.
public abstract class Property {
int propertyId;
}
public <P extends Property> P addProperty(PropertyFactory<P> factory) {
P temp = Engine.createEngineObject(factory.make());
temp.propertyId = properties.size();
return temp;
}
// in Engine.java
public <P extends Property> P createEngineObject(P property) {
// do stuff here
return property;
}
然后使用:
private static final PropertyFactory<ExampleProperty> EXAMPLE_MAKER = ExampleProperty::new;
ExampleProperty ep = addProperty(EXAMPLE_MAKER);