用于Java数组的包装类,用于其元素的通用实例化

时间:2013-06-05 06:48:01

标签: java arrays generics types garbage-collection

我目前正在用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中实例化。所以现在我的问题是:有没有解决方法呢?

我不想在更新渲染方法中分配新对象,因为我害怕垃圾收集器会对帧速率做什么不时:)

3 个答案:

答案 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构造函数) ,但既然你提前知道如何构建所有具体的子类,我建议在此基础上合并一些代码(假设BulletWall的{​​{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