我目前正在用Java编写游戏。 在这个游戏中,我使用数组作为子弹之类的东西的数据结构。 现在我无法使用泛型类型在数组周围编写包装类。 我构建了以下示例来演示我的问题。
public class CoolArray< E extends MyInterface > {
private final E[] array;
public int aliveElements;
public CoolArray( final Class< ? extends MyInterface > clazz, int size ) {
array = (E[])Array.newInstance( clazz, size );
for ( int i = 0; i < array.length; i++ ) {
// i would like to instantiate all array elements
// to avoid constant object allocation at runtime
try {
array[i] = clazz.newInstance(); // always throws NullPointerException
} catch ( Exception e ) { } // omitted
}
aliveElements= 0;
}
public E get( int i ) {
return array[ i ];
}
// rest omitted ...
}
这应该是我的数组包装类。我在加载一个级别时创建这个数组。在游戏的 update 和 render 方法中,我只迭代 aliveElements - 数组的元素。
MyInterface包含方法存根,例如 update 和 render ......
现在,每当我射击子弹时,我会在位置 aliveElements 的元素上调用一些 init 方法。我这样做是为了避免垃圾回收。
myCoolArray.get( myCoolArray.aliveElements ).init( ... )
这是由于 ClassCastException 而失败的部分,无论我如何尝试实例化数组中的每个元素。
注意:我很清楚在上面的例子中我会得到一个 NullPointerException 因为我没有实例化这些元素!那不是问题。
谷歌,一些书籍和其他问题告诉我,泛型类型无法在Java中实例化。所以现在我的问题是:有没有解决方法呢?
我不想在更新或渲染方法中分配新对象,因为我害怕垃圾收集器会对帧速率做什么不时:)
答案 0 :(得分:3)
首先:你编写的类几乎只是ArrayList
,对元素类型有额外的限制。试试你是否可以使用它(或包装它)。即使您最终没有这样做,实施Collection<E>
或至少 Iterable<E>
也应该有助于编写可重复使用的代码。
下一篇:你说你解决了NullPointerException
,但没告诉我们你是如何实例化对象的。
是的,有一种方法无法执行new E()
,这涉及到Class<E
左右,幸运的是你已经拥有:你只需使用c.newInstance()
来填充你的内部数组(如果你的MyInterface
实现都有公共的无参数构造函数)。
答案 1 :(得分:1)
我想通了,当我拨打InstantiationException
时,为什么我会收到array[i] = clazz.newInstance()
。
这是因为传递的Class I没有默认的Constructor。
该课程如下:
public class MyClass implements MyInterface {
SomeField someField;
public MyClass( SomeField someField ) {
this.someField = someField;
}
// omitted
}
为了让它运行,我将MyInterface改为abstract class
,我延伸而不是继承。我将带有Constructor
参数的SomeField
放入Superclass
,并出于安全原因将默认构造函数设为私有。
然后我更改了使用SomeField
参数调用构造函数的代码实例化泛型类的代码。
以下是解决方案所有类的完整代码。由于我在这个问题上工作了很长时间,我想我在这里分享了所有这些,希望能帮助别人解决问题......
新的超类:
public abstract class Superclass {
protected SomeField someField;
/**
* DO NOT use this constructor! Just don't!
*/
@SuppressWarnings( "unused" )
private Superclass() {
}
public Superclass( SomeField someField ) {
this.someField = someField;
}
public abstract void update( float delta );
public abstract void render();
}
新的通用数组实现:
public class QuickArray< E extends Superclass > {
private final E[] array;
private int elementCount;
@SuppressWarnings( { "unchecked" } )
public QuickArray( final Class< E > clazz, final SomeField someField, final int size ) {
array = (E[])Array.newInstance( clazz, size );
for ( int i = 0; i < array.length; i++ ) {
try {
array[ i ] = clazz.getDeclaredConstructor( SomeField.class ).newInstance( someField );
} catch ( Exception e ) {
e.printStackTrace();
}
}
elementCount = 0;
}
public E get( final int i ) {
return array[ i ];
}
// some other fancy methods
}
要放入数组的测试类:
public class MyClass extends Superclass {
public MyClass( SomeField someField ) {
super( someField );
}
@Override
public void update( float delta ) {
// update
}
@Override
public void render() {
// render
}
// some other fancy methods
}
以下是数组的初始化方式:
SomeField someField = new SomeField();
QuickArray< MyClass > myArray = new QuickArray< MyClass >( MyClass.class, someField, CAPACITY );
答案 2 :(得分:0)
你是正确的,你不能在构造函数的循环中执行new E()
(因为编译器不能保证对E
的所有可能值都有一个no-arg构造函数) ,但既然你提前知道如何构建所有具体的子类,我建议在此基础上合并一些代码(假设Bullet
和Wall
的{{1}}子类:< / p>
MyInterface
您可能更喜欢使用多态来决定实例化哪个子类。然后你创建
private static <C extends MyInterface> C create (Class<C> c) {
MyInterface created;
if (c == Bullet.class) {
created = new Bullet ();
} else if (c == Wall.class) {
created = new Wall ();
} else {
throw new IllegalArgumentException ("Unknown class " + c);
}
return c.cast (created);
}
等等,并将正确的interface Factory<E extends MyInterface> {
E create();
}
class BulletFactory implements Factory<Bullet> {
public Bullet create() {
return new Bullet();
}
}
对象传递给构造函数。
最后,我注意到你传入的Factory
对象没有被正确约束来创建类的泛型参数,构造函数应该被定义为
Class
或
public CoolArray(final Class<E> clazz, int size) {
array = (E[]) Array.newInstance(clazz, size);
for (int i = 0; i < array.length; i++) {
array[i] = GenericFactory.create(clazz);
}
aliveElements= 0;
}
如果您决定使用工厂方法。
(您也不需要在public CoolArray(final Class<E> clazz, final Factory<E> factory, int size) {
array = (E[]) Array.newInstance(clazz, size);
for (int i = 0; i < array.length; i++ ) {
array[i] = factory.create(); }
aliveElements= 0;
}
方法中将广播投放到E
。