2013-06-05 40 views
2

我目前正在用Java写一个游戏。 在这个游戏中,我使用数组作为子弹等东西的数据结构。 现在我没有用数组编写一个使用泛型类型的包装类。 我构建了以下示例来演示我的问题。包装类的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 ... 
    } 

这应该是我的数组包装类。我在加载关卡时创建这个数组。在更新渲染游戏的方法,我迭代只有aliveElements - 数组的元素。

MyInterface的contains方法存根如更新渲染 ...

现在,每当我拍了子弹,我呼吁元素一些初始化方法在位置aliveElements。我这样做是为了避免垃圾收集。

myCoolArray.get(myCoolArray.aliveElements).init(...) 

这是失败,因为一个ClassCastException异常的不管我怎么尝试实例数组中的每一个元素的一部分。

注:我很清楚,在上面的例子中,我将得到一个空指针异常,因为我没有实例化元素!这不是问题。

谷歌,一些书籍和其他问题在这里告诉我,泛型类型不能在Java中实例化。所以现在我的问题是:有没有解决这个问题的方法?

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

回答

1

我想通了,为什么当我拨打array[i] = clazz.newInstance()时得到InstantiationException。 这是因为我传入的类没有默认的构造函数。 类的样子:

public class MyClass implements MyInterface { 
     SomeField someField; 
     public MyClass(SomeField someField) { 
      this.someField = someField; 
     } 
     // omitted 
    } 

要让它运行,我改变MyInterface的是一个abstract class我去继承继承而来。我把ConstructorSomeField参数放到那个Superclass中,并且出于安全原因将默认构造函数设为private。

然后,我更改了代码,其中用通过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); 
0

你是正确的,你不能在你的构造函数的循环中执行new E()(因为编译器不能保证所有可能的值为E都没有无参数构造函数),但是由于你提前知道如何构造你所有的具体子类,我建议合并一些代码沿线(假设BulletWall子类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; 
} 
如果您决定使用工厂方法,则为

(你也不必投来Eget方法。)

+0

感谢您的回复。这看起来很复杂,应该很简单。我没有尝试过,因为我知道我整个过程中做错了什么。尽管我感谢你的努力! – grAPPfruit

3

首先:你写的类非常简单,只是ArrayList与元素类型的附加限制。试试如果你可以使用它(或包装它)。即使你最终没有这样做,执行Collection<E>至少Iterable<E>应该有助于编写可重用的代码。

下一步:你说你解决了NullPointerException但不告诉我们你是如何实例化你的对象的。

是的,是有办法解决不能够做new E(),其中涉及具有Class<E左右,其中幸运的是你已经有了:你可以简单地使用c.newInstance()填写您的内部阵列(如果你MyInterface的实现都具有公共无参数构造函数)。

+0

谢谢你的回答!那么,你对ArrayList部分不正确。由于插入和删除元素时的性能问题,我不想使用ArrayLists。特别是当从* update()*和* render()*每次调用多次时。 嗯,我不是说我解决了'NullPointerException'我只是想知道我必须在那里编写它才能工作。 你是绝对正确的,它是'array [i] .newInstance()'。 但是当我写这个,我有一个'InstantiationError'。那是我想要修复的那个。 – grAPPfruit

+0

您可以使用预先填充的ArrayList(就像使用预先填充的数组一样)。我从来没有说过关于'array [i] .newInstance()'的任何内容。什么是InstantiationError的消息?一个'InstantiationError'不应该出现在这里,可能是一个'InstantiationException'。那是怎么回事?最后但并非最不重要的是:发布[SSCCE](http://sscce.org),因为它看起来有很多我们没有的信息(但我们需要它来帮助你)。 –

+0

我真的很感激你对解决我的问题的热情!但我只是自己贴出解决方案来解决我的问题。 你说得对,我的意思是'InstantiationException',它看起来像'java.lang.InstantiationException:generics.MyClass'。它指向我调用'array [i] = clazz.newInstance();' 这就是为什么我认为这行中一定有错误,但它是我的类的构造函数导致该错误。我下面的详细解释不应该留下任何问题。 – grAPPfruit