【问题标题】:Running a Static Block Without Instantiating a Class - Java在不实例化类的情况下运行静态块 - Java
【发布时间】:2014-11-21 23:13:37
【问题描述】:

我有几个子类扩展了一个抽象父类。我希望父类有一个静态 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());
    }    
}

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

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

【问题讨论】:

标签: java classloader static-block


【解决方案1】:

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

【讨论】:

  • 感谢您的快速回复。看来我终于有借口学习反射了 :) 实用函数不需要事先知道子类的名称吧?
  • @Camander,不,您不需要知道名称,在扫描包时您会获取类对象,使用方法Class#isAssignableFrom(java.lang.Class) 来确定类是否是调用类的子类型:Parent.class.AssignableFrom(protetialChild),如果此调用返回true,那么您找到了一个孩子,创建一个实例并将其添加到列表中。
【解决方案2】:

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

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

这是一个可以做到这一点的 sn-p。请记住:因为您的列表绑定到 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;
    }
}

【讨论】:

    【解决方案3】:

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

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-30
      • 1970-01-01
      • 2012-06-03
      • 1970-01-01
      • 2014-06-18
      • 1970-01-01
      • 2016-11-18
      • 2014-02-03
      相关资源
      最近更新 更多