英文:
How do I generalize this?
问题
我有一个名为ExampleProperty
的类,它继承自超类Property
,在另一个名为GameObject
的类中,我有一个ArrayList<Property> properties
,以及以下方法,其中Engine.createEngineObject(new ExampleProperty())
仅将new ExampleProperty()
添加到列表中并将其返回,propertyId
是Property
的继承成员,而properties
是Property
列表:
public ExampleProperty addExampleProperty() {
ExampleProperty temp = (ExampleProperty)Engine.createEngineObject(new ExampleProperty());
temp.propertyId = properties.size();
return temp;
}
我想将此方法泛化,以便我可以传递一个Class<?> propertyClass
,并使其返回与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 temp;
}
英文:
I have a class ExampleProperty
that extends the superclass Property
and in a separate class, GameObject
, I have an ArrayList<Property> properties
as well as the following method where Engine.createEngineObject(new ExampleProperty())
simply adds the new ExampleProperty()
to a List and returns it back, propertyId
is an inherited member of Property
, and where properties
is the list of Property
s:
public ExampleProperty addExampleProperty()
{
ExampleProperty temp = (ExampleProperty)Engine.createEngineObject(new ExampleProperty());
temp.propertyId = properties.size();
return temp;
}
I want to generalize this method so I can pass in a Class<?> propertyClass
and have it return an object the same type as propertyClass
(and also perform the cast on line 3?). I'm afraid I'm not familiar enough with generalization yet so any help is appreciated
EDIT: This is the revised code after the answer was posted. I forgot to mention a bit of info but I worked through it so here's what I have. It's probably awful but it's mine, it works, and I'm proud of it:
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;
}
答案1
得分: 1
这可能会起作用:
public <T extends Property> T createEngineObject(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
// 做一些操作...
return property;
}
然而,传递Property
实例似乎是一个更好的主意:
public <T extends Property> T createEngineObject(T property) {
// 做一些操作...
return property;
}
英文:
This might do the trick:
public <T extends Property> T createEngineObject(Class<T> propertyClass) throws IllegalAccessException, InstantiationException {
Property property = propertyClass.newInstance();
// do something...
return property;
}
Although, passing Property
instance seems like a better idea:
public <T extends Property> T createEngineObject(T property) {
// do something...
return property;
}
答案2
得分: 0
以下是翻译好的内容:
您在这里的核心问题是,您对ExampleProperty
执行的操作未在类型中捕获。因此,您无法逐字泛化这个问题。
让我们列出您对当前硬编码类型(ExampleProperty)所做的操作,以便我们知道要在类型中捕获什么,以便我们可以将其泛化:
- 您调用它的无参构造函数
- 您将该调用的结果作为第一个参数传递给
Engine.createEngineObject
的调用。 - 您将
createEngineObject
调用的结果转换为此类型。 - 您引用此类型的
propertyId
字段。 - 您返回此类型。
在所有这些操作中,最棘手的操作是第一个操作:构造函数不参与类型系统。您不能在Java类型中声明“具有无参构造函数”。根本不行。
解决这个问题有两种方法:
- 仅在文档中说明,没有任何编译时的保证,并使用
java.lang.Class
实例作为载体,以便代码知道从哪里获取构造函数。对于您将传递到此方法的泛型形式中的任何类型,都不会进行任何编译时检查,以确保其实际上具有无参构造函数,或者是非抽象的。如果不满足这些条件,您唯一会知道的方式就是在运行时会发生异常。 - 使用工厂。
当然,正确的答案是第二个:使用工厂。捕获“可以创建ExampleProperty实例的东西”的概念,因为这在Java的类型系统中是可以传达的。如果有任何适用于整个类型的内容,我们可以将此工厂用作载体:
您的通用代码:
public interface PropertyFactory<P extends Property> {
P make();
}
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;
}
// 在Engine.java中
public <P extends Property> P createEngineObject(P property) {
// 在此处执行操作
return property;
}
然后使用:
private static final PropertyFactory<ExampleProperty> EXAMPLE_MAKER = ExampleProperty::new;
ExampleProperty ep = addProperty(EXAMPLE_MAKER);
英文:
Your central issue here is that the things you are doing to ExampleProperty
aren't captured in a type. Therefore, you can't generalize this verbatim.
Let's list what you 'do' with the currently-hardcoded type (ExampleProperty), so that we know what to capture in types so we can generalize it:
- You invoke its no-args constructor
- You pass the result of that as first arg for a call to
Engine.createEngineObject
. - You cast the result of
createEngineObject
call to this type. - You refer to the
propertyId
field of this type. - You return the type.
Of all those operations, the most problematic one by far is the very first one: Constructors do not participate in the typing system. You can't declare 'has a no-args constructor' in a java type. At all. Whatsoever.
There are two solutions to the problem:
- Leave it stated solely in documentation with zero compile time guarantees, and use a
java.lang.Class
instance as vehicle so that the code knows where to fetch that constructor from. There will be no compile-time checking whatsoever that any type you pass into the genericsed form of this method in fact has a no-args constructor, or that it is non-abstract. If you fail these conditions, the only way you'll ever know is that some exception will occur at runtime. - Use factories.
The right answer is of course the second one: Use factories. Capture the concept of 'a thing that can make ExampleProperty instances', because that can be conveyed in java's type system. We can use this factory as a vehicle for anything that applies to the entire type, if it comes up:
your generalized code:
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;
}
then to use:
private static final PropertyFactory<ExampleProperty> EXAMPLE_MAKER = ExampleProperty::new;
ExampleProperty ep = addProperty(EXAMPLE_MAKER);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论