代码之家  ›  专栏  ›  技术社区  ›  user2427

如何以编程方式获取Java中接口的所有实现的列表?

  •  59
  • user2427  · 技术社区  · 16 年前

    我能用反射或者类似的东西吗?

    11 回复  |  直到 16 年前
        1
  •  46
  •   lakshayg    7 年前

    我已经寻找了一段时间,似乎有不同的方法,这里有一个总结:

    1. reflections

      Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
      Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
      
    2. 服务加载器 (根据埃里克森的回答)看起来是这样的:

      ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
      for (Pet implClass : loader) {
          System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
      }
      

      请注意,要实现这一点,您需要定义 Pet resources/META-INF/services 带着名字 examples.reflections.Pet 并声明 宠物

      examples.reflections.Dog
      examples.reflections.Cat
      
    3. 包级别批注 . 下面是一个例子:

      Package[] packages = Package.getPackages();
      for (Package p : packages) {
          MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
          if (annotation != null) {
              Class<?>[]  implementations = annotation.implementationsOfPet();
              for (Class<?> impl : implementations) {
                  System.out.println(impl.getSimpleName());
              }
          }
      }
      

      以及注释定义:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.PACKAGE)
      public @interface MyPackageAnnotation {
          Class<?>[] implementationsOfPet() default {};
      }
      

      并且必须在名为 package-info.java

      @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
      package examples.reflections;
      

      注意,只有类加载器当时知道的包才会通过调用 Package.getPackages() .

    此外,还有其他基于URLClassLoader的方法,除非执行基于目录的搜索,否则这些方法将始终限于已加载的类。

        2
  •  28
  •   Aldo waxwing    8 年前

    埃里克森说了什么,但如果你还想做,那就看看 Reflections . 从他们的页面:

    使用反射可以查询元数据以获取:

    • 使用一些注释对所有类型进行注释
    • 使用一些注释对所有类型进行注释,包括匹配的注释参数
    • 用一些
        3
  •  26
  •   erickson    5 年前

    如果你想避免这种情况,你需要实现你自己的类文件解析器,它的运行效率更高,而不是反射。字节码工程库可能有助于这种方法。

    这个 Service Provider mechanism ServiceLoader an example 另一个答案。

        4
  •  13
  •   NaN    6 年前

    Spring有一个非常简单的方法来实现这一点:

    public interface ITask {
        void doStuff();
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    

    ITask Spring将用所有实现填充它:

    @Service
    public class TaskService {
    
        @Autowired
        private List<ITask> tasks;
    }
    
        5
  •  4
  •   Zhichao    16 年前

    是的,第一步是确定“所有”你关心的类。如果您已经拥有这些信息,则可以枚举它们中的每一个,并使用instanceof验证关系。相关文章如下: http://www.javaworld.com/javaworld/javatips/jw-javatip113.html

        6
  •  4
  •   Sachin Sachin    16 年前

    埃里克森说的最好。这里有一个相关的问答线索- http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

    Apache BCEL库允许您在不加载类的情况下读取类。我相信它会更快,因为你应该能够跳过验证步骤。使用类加载器加载所有类的另一个问题是,您将受到巨大的内存影响,并且会无意中运行任何您可能不想执行的静态代码块。

    http://jakarta.apache.org/bcel/

        7
  •  2
  •   NaN    6 年前

    一个新版本的@kaybee99的答案,但现在返回用户的要求:实现。。。

    public interface ITask {
        void doStuff();
        default ITask getImplementation() {
           return this;
        }
    
    }
    
    @Component
    public class MyTask implements ITask {
       public void doStuff(){}
    }
    

    然后可以自动连接类型列表 ITask Spring将用所有实现填充它:

    @Service
    public class TaskService {
    
        @Autowired(required = false)
        private List<ITask> tasks;
    
        if ( tasks != null)
        for (ITask<?> taskImpl: tasks) {
            taskImpl.doStuff();
        }   
    }
    
        8
  •  2
  •   Luke Hutchison    5 年前

    ClassGraph 很简单:

    my.package.MyInterface :

    @Grab('io.github.classgraph:classgraph:4.6.18')
    import io.github.classgraph.*
    new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
        scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
    }
    
        9
  •  2
  •   Luke Hutchison    5 年前

    列出实现给定接口的所有类的最健壮机制是 ClassGraph widest possible array of classpath specification mechanisms ,包括新的JPMS模块系统。(我是作者。)

    try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
            .enableClassInfo().scan()) {
        for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
            foundImplementingClass(ci);  // Do something with the ClassInfo object
        }
    }
    
        10
  •  1
  •   Uri    16 年前

    另外,如果您正在编写一个IDE插件(在这个插件中,您试图做的事情相对比较常见),那么IDE通常会为您提供更有效的方法来访问用户代码当前状态的类层次结构。

        11
  •  1
  •   NDK    8 年前

    我遇到了同样的问题。我的解决方案是使用反射来检查ObjectFactory类中的所有方法,消除那些不是createXXX()方法的方法,这些方法返回我绑定的一个pojo的实例。这样发现的每个类都被添加到一个class[]数组中,然后传递给JAXBContext实例化调用。这执行得很好,只需要加载ObjectFactory类,无论如何都将需要它。我只需要维护ObjectFactory类,这是一个手工执行的任务(在我的例子中,因为我从pojo开始并使用schemagen),或者可以根据xjc的需要生成。无论哪种方式,它都是高效的、简单的。