扩展泛型类

时间:2015-07-07 20:07:59

标签: java arrays generics libgdx

我们假设我有一个类Text,它实现了一个名为Drawable的接口,其定义如下:

public interface Drawable {

  public void draw (DrawConfig drawConfig);
}

我希望有一个对象,其作用类似于带有项目的数组,但也实现Drawable,以便能够编写drawables.draw(drawConfig)并将其转发给所有子项,而不是做一个for循环。所以我首先创建了一个包装类,如下所示:

public class Drawables implements Drawable {

  private final Array<Drawable> items = new Array<Drawable>();

  public void add (final Drawable value) {
    this.items.add(value);
  }

  @Override
  public void draw (final DrawConfig drawConfig) {
    for (final T item : this.items)
      item.draw(drawConfig);
  }
}

第一个问题是因为它是一个包装器,它没有任何Array类方法,我必须手动添加我需要的方法(比如上例中的add()

第二个问题是,如果我只存储Text个对象,我就无法写:

for (Text t : drawables)
    t.setText("blablabla");

这意味着我必须制作另一个数组,而我最终只会为此设置两个数组。我想避免这种情况,所以我考虑使用通用性,因为Array类是通用的,我将Drawables类转换为:

public class Drawables<T extends Drawable> extends Array<T> implements Drawable {

  @Override
  public void draw (final DrawConfig drawConfig) {
    for (final T item : this.items) // Line 17
      item.draw(drawConfig);
  }
}

现在我可以写:

Drawables<Text> drawables = new Drawables<Text>();

drawables.add(new Text());
drawables.add(new Text());

for (Text t : drawables)
    t.setText("blablabla");

drawables.draw(drawConfig);

它编译!! 除了在运行时我收到以下错误:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [La.b.c.ui.Drawable;
    at a.b.c.Drawables.draw(Drawables.java:17)
    at a.b.c.ui.components.Text_UseTest.render(Text_UseTest.java:80)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)

请参阅我的代码中第17行的注释。

有什么问题?是否有可能实现我想做的事情?

3 个答案:

答案 0 :(得分:2)

我认为你没有正确的设计。 Drawable应该只在其合同中指定它根据某个配置绘制,而不是分离绘制单个项目和绘制多个项目。它是如何做到这一点的,或者它用来做这件事的其他对象并不重要。

因此,我会先这样做:

public interface Drawable {
    void draw (DrawConfig drawConfig);    
}

现在看来您希望Drawable维护私人会员。这个私人成员对事物的绘制有一些影响。有趣的是,它可能是被绘制的东西,或者它可能包含需要绘制的东西,或者它可能是完全不同的东西。目前我们还不知道 会影响draw的行为。那我们该如何处理呢?好吧,我们可以创建一个抽象类来维护这个私有成员,并实现Drawable

public abstract class AbstractDrawable<T> implements Drawable {    
    protected T data;           
}

请注意,draw没有实施。这是故意的,因为只有在具体实现中我们才会真正定义draw的行为。您现在可以很容易地看到我们如何分解两个问题(维护状态和实现行为)。现在我们可以定义一个具体的实现,它定义 它将如何维护状态,以及 它将如何实现行为:

public class Drawables<T extends Drawable> extends AbstractDrawable<List<T>> {

    public Drawables() {
        this.data = new ArrayList<T>();
    }

    public void addDrawable(T item) {
        this.data.add(item);
    }

    @Override
    public void draw (final DrawConfig drawConfig) {
        for (final T item : this.data) {
            item.draw(drawConfig);
        }
    }
}

这里我们有一个非常具体的AbstractDrawable<T>实现,它本身与一个drawable列表一起工作。这个具体实现已经定义了它如何管理状态(它使用Drawable实例的内部列表)以及它如何管理行为(它的“绘制”行为是“绘制”它的每个Drawable实例维护)。

你提到你想要所有列表方法等。扩展一个类只是为了访问它的方法不是一个好主意。您的可绘制项目列表实际上不是一个列表。它是一个可绘制的,恰好与drawables列表一起使用。上面的方法隐式使用组合而不是继承,因为AbstractDrawable<T>封装了一些将以某种方式绘制的内部数据(不重要如何这样做;只是它已经完成)。因此,您现在可以维护一个内部列表,然后以您选择的方式“绘制”,而不是扩展List<T>。这正是具体实现Drawables<T extends Drawable>中发生的事情。

您的下一个问题是无法在t.setText()个实例上调用Text。您可以执行此操作的唯一方法是,如果您具有专门针对Text实例的具体实例,或者您在instanceof内执行了draw检查。这是因为你拥有的只是T extends Drawable;该类知道它是某种Drawable,它能够draw。除此之外,它不知道具体的实现。

答案 1 :(得分:1)

类Array实现了Iterable。所以你可以这样写。

@Override
public void draw (final DrawConfig drawConfig) {
  for (final T item : this) // Line 17
      item.draw(drawConfig);
}

答案 2 :(得分:0)

擦除后的问题:

for (final Drawable item : this.items) // this.items is of Object[]!!!

现在for循环不知道如何转换(在java中没有隐式转换)。你不能简单地写Drawable item = new Object();。你必须自己做演员:

for (Object o : this.items) {
  Drawable item = (Drawable) o;
  item.draw(drawConfig);
}

相关:Cast element in Java For Each statement