在Java中实例化大量子类的最有效方法?

时间:2018-11-20 18:24:24

标签: java class dynamic reflection loading

我正在一个项目中,该项目需要使用扩展特定抽象类的大量类对象。手动实例化需要调用每个对象的单个构造函数,这开始污染我的客户端类。

该项目的原始代码太长,无法在此站点上发布,因此下面有一个简化的示例:

假设有一个称为Letter的抽象类:

public abstract class Letter{
    String letter;
}

还有26个单独的字母类,它们扩展了Letter抽象类:

public class A extends Letter{
    this.letter = "A";
}

public class B extends Letter{
    this.letter = "B";
}
... and so on

这是我寻求帮助的地方-有一个名为Alphabet的客户端类:

public class Alphabet{
    public List<Letter> getLetters(){
        List<Letter> letters = new ArrayList<>();
        A a = new A();
        B b = new B();
        C c = new C();
        list.add(a);
        list.add(b);
        list.add(c);
        ...the list goes on

        return letters;
    }
}

如您所见,手动实例化每个字母并不理想。我正在寻找一种在运行时动态创建类对象列表的方法。

也许与此类似:

public class Alphabet{
    public List<Letter> getLetters(){
        List<Letter> letters = getAllClassesExtending(Letter.class);
        return letters;
        //list would then contain A,B,C,D,E,F,G, etc.
    }
}

我最终希望能够创建一个新类并将其自动添加到客户端类的对象列表中,而无需修改客户端类(例如,我不想显式引用每个字母客户类)。

2 个答案:

答案 0 :(得分:1)

这听起来像enum的典型用例。 enum的常量可以具有带有代码的类主体,覆盖基本类型的方法,甚至声明新字段。

获取所有子类型的实例的任务暗含了一组封闭的子类型,这些子类型与enum类型的行为相匹配。

public enum Letter {
    A {
        @Override
        public boolean isVowel() {
            return true;
        }
    },
    B, C, D,
    E {
        @Override
        public boolean isVowel() {
            return true;
        }
    },
    F, G, H,
    I {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, J, K, L, M, N,
    O {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, P, Q, R, S, T,
    U {
        @Override
        public boolean isVowel() {
            return true;
        }
    }, V, W, X, Y, Z;
    ;
    String letter = name();

    public static List<Letter> getLetters() {
        return Arrays.asList(values());
    }

    public boolean isVowel() {
        return false;
    }
}

这表明可以使用重写方法,并且免费提供了用于获取所有实例(作为数组)的方法。此外,名称是在内部指定的,没有多余的内容。

如果您想拥有一组开放的子类,以便在编译代码后进行扩展,则应查看Service Provider conceptServiceLoader类。这种机制不会自动找到子类,而只会自动找到在META-INF的文件中或通过模块声明声明为服务提供者的子类,另一方面,它为您提供了一个可靠的,已建立的解决方案,该子类将由Java开发人员甚至会随着平台的发展而发展,例如与新模块概念的集成。

答案 1 :(得分:0)

您可以使用Reflections库在运行时查找类,例如如果它们全部位于com.letters包下,并且具有无参数构造函数:

public List<? extends Letter> getLetters(){
  Reflections reflections = new Reflections("com.letters");
  Set<Class<? extends Letter>> classes = reflections.getSubTypesOf(Letter.class);
  return classes.stream()
        .map(clazz -> {
            try {
                return clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
        })
        .collect(Collectors.toList());
}

这不是查找类的最有效方法,如果此代码位于关键路径上,则可能要缓存结果。