去重这段重复的Java代码

5

我有大约10个类,每个类都有一个LUMP_INDEX和SIZE的静态常量。 我想要每个这些类的数组,其中数组的大小是使用这两个常量计算出来的。 目前,我为每个类都有一个创建数组的函数,大概是这样的:

private Plane[] readPlanes()
{
    int count = header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE;
    Plane[] planes = new Plane[count];
    for(int i = 0; i < count; i++)
        planes[i] = new Plane();

    return planes;
}

private Node[] readNodes()
{
    int count = header.lumps[Node.LUMP_INDEX].filelen / Node.SIZE;
    Node[] nodes = new Node[count];
    for(int i = 0; i < count; i++)
        nodes[i] = new Node();

    return nodes;
}

private Leaf[] readLeaves()
{
    int count = header.lumps[Leaf.LUMP_INDEX].filelen / Leaf.SIZE;
    Leaf[] leaves = new Leaf[count];
    for(int i = 0; i < count; i++)
        leaves[i] = new Leaf();

    return leaves;
}

等等。 有10个这样的函数,唯一的区别是类类型,所以你可以看到,有很多重复。

有没有人有什么想法来避免这种重复? 谢谢。 (我之前问过类似的问题,但我猜我问错了)


你在void方法中使用了返回语句? - user unknown
这些方法中的 header 对象是什么? - Brian Roach
为什么你不会使用ArrayList呢? - bancer
4个回答

3
使用 Java泛型。这样,您只需编写一个通用方法,并在每次使用时指定类型参数即可。

你的意思是类似于 private <T> void read(Class<Lump> clazz) 吗?我无法通过 clazz 参数访问这两个常量。 - terryhau
2
请注意,您不能使用泛型执行 new T[x] 操作。您可以绕过此问题,但答案并不简单。 - Brian Roach
@Brian Roach:那用什么呢?通用容器? - xtofl
1
@Bala的泛型示例展示了如何解决T[]问题。个人而言,我认为我会使用一个包含ArrayList的方法来代替直接操作数组。 - Brian Roach

2

Bala的解决方案已经很接近了。然而你不能从泛型类型中访问常量,所以我会创建一个getCount()方法(或其他你想要的名字),并让每个子类型实现它,使用相应的常量。

interface LumpySize<L extends LumpySize> {
    int getCount(); // subtypes return the appropriate header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE; 

    T[] initializeArray();

    abstract <T extends LumpySize> static class Base implements LumpySize<T> {
        protected T[] initializeArray(Class<T> cls) {
            int count = getCount();
            T[] lumps = (T[]) Array.newInstance(cls, count);
            for(int i = 0; i < count; i++) {
                try {
                    lumps[i] = cls.newInstance();
                } catch (Exception e) {  // obviously this isn't good practice.
                    throw new RuntimeException(e);
                }
            }
            return lumps;
        }    
    }            
}

class Plane extends LumpySize.Base<Plane> {
    public int getCount() {
        return header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE; // assuming header is available somewhere
    }
    public Plane[] initializeArray() { return initializeArray(Plane.class); }
}

你误读了代码。这是一个抽象基类,你需要扩展它,例如:class Plane extends LumpySize.Base<Plane> - A Lee
+1。没错,这是将实现相关常量引入通用代码的方法。知道这点非常有用。 - extraneon
是的...我只是不知道我是否喜欢它。你可以在接口外部同样容易地定义相同的抽象类,而且实际上并不需要,因为“header”可以封装所有功能。数学仍然在每个类(Plane、Node等)中完成。我的方法将所有这些功能都移到了“header”的实例所在的类内部。 - Brian Roach

1
好的...我已经测试过了,我相信它可以做到你想要的。
你需要一个接口:
public interface MyInterface
{
    public int getSize();
    public int getLumpIndex();
}

你的类实现了该接口:

public class Plane implements MyInterface
{

    ...
    public int getSize()
    {
        return SIZE;
    }

    public int getLumpIndex()
    {
        return LUMP_INDEX;
    }

}

header 是一个实例的类中,你有...
public <E extends MyInterface> E[] 
    getArray(Class<E> c, MyInterface foo)
{
    int count = lumps[foo.getLumpIndex()].filelen / foo.getSize();
    E[] myArray = (E[]) Array.newInstance(c, count);
    for(int i = 0; i < count; i++)
         myArray[i] = c.newInstance();
    return myArray;
}

你可以在你的 Plane 类中这样调用它:

Plane[] p = header.getArray(Plane.class, this);

认为? :) 有人能看一下这个,看看我是否偏离了吗?

(编辑:因为我现在已经测试过了 - 那个可以工作)

另外,您可以通过使getArray()接受大小和索引作为参数来消除每个类中的getter:

public <E extends MyInterface> E[] 
    getArray(Class<E> c, int size, int index)
{
    int count = lumps[index].filelen / size;
    E[] myArray = (E[]) Array.newInstance(c, count);
    for(int i = 0; i < count; i++)
         myArray[i] = c.newInstance();
    return myArray;
}

并将其称为:

Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);

从类内部。接口只是变为空,以提供通用类型,您不必定义getter方法。

或者(我保证这是最后一次编辑,但这确实给了您选择并解释了一些关于泛型的内容)

放弃接口。这样做会删除一些健全性检查,因为该方法不关心您提供的对象类型:

public <E> E[] 
    getArray(Class<E> c, int size, int index)
{
    ...

现在你不需要定义接口或实现它,只需调用:
Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);

谢谢,我认为这可能是最好的方法(除了getArray函数不需要在头文件中)。然而,当我尝试它时,c.newInstance()会抛出一个InstantiationException异常,我不知道为什么,所以我被难住了。 - terryhau
你从未发布过对象的构造函数是什么。你有一个无参数的构造函数吗? - Brian Roach
是的,它们没有参数。我已经找出为什么会抛出异常了。谢谢。 - terryhau

0

使用泛型,但您需要传递某种工厂对象来构造实例以放入集合中,例如:

public class MyClass {

public <E> E[] getArray(IObjectFactory builder, int index, int size){
    ArrayList<E> arrayList = new ArrayList<E>();
    int count = header.lumps[index].filelen / size;//wasn'tsure where header was coming from...
    for(int i = 0; i< count; i++){
        E newInstance = builder.getNewInstance();
        arrayList.add(newInstance);
    }
    return (E[]) arrayList.toArray();
  }   
}    

interface IObjectFactory {
<E> E getNewInstance();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接