0

我有几个子类扩展了一个抽象父类。我希望父类有一个静态 ArrayList 持有每个孩子的实例。如果可能的话,我希望能够添加更多的子类,而不必更改父类的代码。

我想出的一个解决方案是给每个子类一个静态块,它将自己的一个实例添加到 ArrayList。唯一的问题是我需要对每个孩子进行一些调用以使其加载并运行该块。

abstract public class Parent {
    protected static ArrayList<Parent> children = new ArrayList<Parent>();
}

public class ChildA extends Parent {
    static {
        children.add(new ChildA());
    }    
}

public class ChildB extends Parent {
    static {
        children.add(new ChildB());
    }    
}

有没有解决的办法?我可以加载一个类而不调用它吗?有没有更好的方法来做到这一点?

编辑:这实际上是针对基于瓷砖的游戏。我有一个抽象的“瓷砖”类和任意数量的瓷砖类型扩展它。我希望能够轻松地向游戏中添加新图块,而无需到处更改代码。如果您对如何执行此操作有更好的想法,我愿意接受建议。

4

3 回答 3

0

您可以在类层次结构之外执行此操作。有一个实用函数,它使用反射来查找 Parent 的所有后代,然后将它们添加到您的列表中。

于 2014-11-21T23:18:08.517 回答
0

您想要这样做的动机尚不清楚,但有些解决方案不一定涉及反思。

将创建子类的责任转移到工厂类中。在该工厂类中,将子类也插入到您的列表中。

这是一个可以做到这一点的片段。请记住:因为您的列表绑定到Parent,所以您必须转换为正确的子类类型才能稍后实际使用这些类。

public final class ChildFactory {

    private static ChildFactory instance = new ChildFactory();

    private ChildFactory() {

    }

    public <C extends Parent> C generateChild(Class<C> childClass) {
        try {
            final C child = childClass.newInstance();
            Parent.children.add(child);
            return child;
        } catch(InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static ChildFactory getInstance() {
        return instance;
    }
}
于 2014-11-21T23:30:00.627 回答
0

我认为这可能是一种更简单的方法,但这个类可能很有用:

package classFinder;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassFinder {
    protected static Class<?> getClass(String prefix, String classPath)
            throws ClassNotFoundException {
        if (!classPath.endsWith(".class") || !classPath.startsWith(prefix)) {
            return null;
        }
        return Class.forName(classPath.substring(prefix.length(),
                classPath.length() - ".class".length()).replace('/', '.'));
    }

    protected static Class<?> getClass(File rootFile, File classFile)
            throws ClassNotFoundException {
        return getClass(rootFile.getPath() + '/', classFile.getPath());
    }

    @SuppressWarnings("unchecked")
    protected static <T> Set<Class<T>> searchAllSubclassesInDirectory(
            File rootFile, File searchFile, Class<?> cls,
            boolean abstractClasses) throws ClassFinderException {
        Set<Class<T>> result = new HashSet<Class<T>>();
        if (searchFile.isDirectory()) {
            for (File file : searchFile.listFiles()) {
                result.addAll(ClassFinder.<T> searchAllSubclasses(
                        rootFile.getPath(), file.getPath(), cls,
                        abstractClasses));
            }
            return result;
        }

        String fileName = searchFile.getName();
        if (!fileName.endsWith(".class")) {
            return result;
        }
        try {
            Class<?> entry = getClass(rootFile, searchFile);
            if (entry != null
                    && (abstractClasses || !Modifier.isAbstract(entry
                            .getModifiers()))) {
                Class<?> superClass = entry;
                while (!((superClass = superClass.getSuperclass()) == null)) {
                    if (superClass.equals(cls)) {
                        result.add((Class<T>) entry);
                        return result;
                    }
                }
            }
            return result;
        } catch (ClassNotFoundException e) {
            // e.printStackTrace(); //DEBUG only
            return result;
        }
    }

    @SuppressWarnings("unchecked")
    protected static <T> Set<Class<T>> searchAllSubclassesInJar(File jar,
            Class<?> cls, boolean abstractClasses) {
        Set<Class<T>> result = new HashSet<Class<T>>();
        try {
            JarFile jarFile = new JarFile(jar);
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry file = entries.nextElement();
                if (file.isDirectory()) {
                    continue;
                }
                Class<?> entry = getClass("", file.getName());
                if (entry != null
                        && (abstractClasses || !Modifier.isAbstract(entry
                                .getModifiers()))) {
                    Class<?> superClass = entry;
                    while (!((superClass = superClass.getSuperclass()) == null)) {
                        if (superClass.equals(cls)) {
                            result.add((Class<T>) entry);
                        }
                    }
                }
            }
            jarFile.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    protected static <T> Set<Class<T>> searchAllSubclasses(String rootPath,
            String searchPath, Class<?> cls, boolean abstractClasses)
            throws ClassFinderException {
        if (searchPath.endsWith(".jar")) {
            return searchAllSubclassesInJar(new File(searchPath), cls,
                    abstractClasses);
            // return new HashSet<Class<T>>();
        } else {
            return searchAllSubclassesInDirectory(new File(rootPath), new File(
                    searchPath), cls, abstractClasses);
        }
    }

    // TODO create public method to search inside a not root directory/package

    public static <T> Set<Class<T>> searchAllSubclasses(Class<?> cls,
            boolean abstractClasses) throws ClassFinderException {
        Set<Class<T>> result = new HashSet<Class<T>>();
        String classpath = System.getProperty("java.class.path");
        for (String path : classpath
                .split(System.getProperty("path.separator"))) {
            result.addAll(ClassFinder.<T> searchAllSubclasses(path, path, cls,
                    abstractClasses));
        }

        return result;
        // return ClassFinder.<T> searchAllSubclasses(ROOT_URL, cls,
        // abstractClasses, "");
    }
}

如果您正在使用标准的 java 桌面应用程序,它可能会工作。此类在程序目录树上搜索给定超类的实现。也适用于 jar 文件。

然后,您可以初始化您的静态字段:

abstract public class Parent {
    protected static Set<Parent> children = ClassFinder
                .<Parent> searchAllSubclasses(Parent.class, true);
}
于 2014-11-21T23:43:35.220 回答