获取指定包下的所有子类(不包括父类)

获取指定包下的所有子类(不包括父类)

如果您的需求是获取某个包下的所有子类(即存在父类也在同一包中的类),而排除那些没有被子类继承的类,可以使用以下实现:

完整实现代码

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class SubclassFinder {

    /**
     * 获取指定包下的所有子类(不包括没有子类的父类)
     * @param packageName 包名(如:"com.example.entities")
     * @return 子类的Class对象集合
     */
    public static Set<Class<?>> findSubclasses(String packageName) {
        Set<Class<?>> allClasses = getClasses(packageName);
        Set<Class<?>> subClasses = new HashSet<>();
        Set<Class<?>> parentClasses = new HashSet<>();

        // 第一次遍历:收集所有父类
        for (Class<?> clazz : allClasses) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && allClasses.contains(superClass)) {
                parentClasses.add(superClass);
            }
        }

        // 第二次遍历:收集所有子类(其父类在同一个包中)
        for (Class<?> clazz : allClasses) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && allClasses.contains(superClass)) {
                subClasses.add(clazz);
            }
        }

        return subClasses;
    }

    /**
     * 获取包下的所有类
     */
    private static Set<Class<?>> getClasses(String packageName) {
        Set<Class<?>> classes = new HashSet<>();
        String path = packageName.replace('.', '/');
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        
        try {
            Enumeration<URL> resources = classLoader.getResources(path);
            List<File> dirs = new ArrayList<>();
            
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                dirs.add(new File(resource.getFile()));
            }
            
            for (File directory : dirs) {
                classes.addAll(findClasses(directory, packageName));
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        return classes;
    }

    /**
     * 递归查找目录中的类
     */
    private static Set<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
        Set<Class<?>> classes = new HashSet<>();
        if (!directory.exists()) {
            return classes;
        }
        
        File[] files = directory.listFiles();
        if (files == null) {
            return classes;
        }
        
        for (File file : files) {
            if (file.isDirectory()) {
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                Class<?> clazz = Class.forName(className);
                classes.add(clazz);
            }
        }
        return classes;
    }

    public static void main(String[] args) {
        // 示例:获取com.example.entities包下的所有子类
        Set<Class<?>> subClasses = findSubclasses("com.utils.entity.mongo");
        Set<Class<?>> supportClass = new HashSet<>();
        System.out.println("子类列表:");
        for (Class<?> clazz : subClasses) {
            supportClass.add(clazz.getSuperclass());
        }
        subClasses.removeAll(supportClass);
        for (Class<?> clazz : subClasses) {
            System.out.println(clazz.getName());
        }
    }
}

注意事项

  1. 类加载:此方法会实际加载所有找到的类
  2. 性能考虑:对于大型项目,扫描所有类可能较慢
  3. JAR文件:如果类在JAR文件中,需要使用不同的资源访问方式
  4. Spring环境:如果是Spring项目,可以使用更高效的类扫描机制

Spring Boot 替代方案

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class SpringSubclassScanner {

    public Set<Class<?>> findSubclasses(String basePackage, Class<?> parentClass) throws ClassNotFoundException {
        ClassPathScanningCandidateComponentProvider scanner = 
            new ClassPathScanningCandidateComponentProvider(false);
        
        scanner.addIncludeFilter(new AssignableTypeFilter(parentClass));
        
        return scanner.findCandidateComponents(basePackage).stream()
            .map(beanDefinition -> {
                try {
                    return Class.forName(beanDefinition.getBeanClassName());
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            })
            .filter(clazz -> !clazz.equals(parentClass)) // 排除父类本身
            .collect(Collectors.toSet());
    }
}

这种方法可以查找指定父类的所有子类,无论子类在哪个包中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆芽脚脚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值