如何获取某个类的所有子类

引言

在我们写工厂类的时候,可能会根据不同的类型(type)生成不同的对象。但在工厂类初始化的时候,我们需要将某个类型的所有类全部初始化才能达到我们的目的。
举个例子,我们定义了很多动物(Animal),我们需要一个AnimalFactory根据动物类型(type)去构建不同的动物实例。如下代码所示:
我们先构建一个动物基础类型,包含两个方法:

  • getType:获取动物类型
  • train:训练动物
public interface IAnimal {
    	/**
    	 * 获取动物种类
    	 * @return
    	 */
    	int getType();
    
    	/**
    	 * 训练动作
    	 */
    	void train();
    }

然后我们定义了多个动物实现IAnimal接口

public class TDog implements IAnimal {
	/**
	 * 获取动物种类
	 *
	 * @return
	 */
	@Override
	public int getType() {
		return 1;
	}

	/**
	 * 训练动作
	 */
	@Override
	public void train() {
		System.out.println("握手");
	}
}

public class TLion implements IAnimal{

	/**
	 * 获取动物种类
	 *
	 * @return
	 */
	@Override
	public int getType() {
		return 1;
	}

	/**
	 * 训练动作
	 */
	@Override
	public void train() {
		System.out.println("钻火圈");
	}
}

接下来我们定义一个工厂类(TAnimalFactory),用来创建不同的动物

@Service
public class TAnimalFactory {
	private static List> animalLists = Lists.newArrayList();
	private static Map> animalMaps = Maps.newHashMap();

	static {
		animalLists.add(TDog.class);
		animalLists.add(TLion.class);
	}

	@PostConstruct
	public void init() throws IllegalAccessException, InstantiationException {
		for (Class clazz : animalLists){
			IAnimal obj = clazz.newInstance();
			animalMaps.put(obj.getType(), clazz);
		}
	}

	/**
	 * 构建动物类
	 * @param type
	 * @return
	 */
	IAnimal build(int type) throws IllegalAccessException, InstantiationException {
		return animalMaps.get(type).newInstance();
	}
}

有了工厂类,我们就可以根据动物类型获取不同的动物实例。

存在的问题
上述工厂类可以解决我们大部分问题,但是当我们新增一种动物类型时(Cat),我们不但要创建一个动物类,还需要再工厂类(AnimalFactory)里进行注册

public class TCat implements IAnimal {
	/**
	 * 获取动物种类
	 *
	 * @return
	 */
	@Override
	public int getType() {
		return 3;
	}

	/**
	 * 训练动作
	 */
	@Override
	public void train() {
		System.out.println("打滚");
	}
}

在工厂类里注册这种类型

static {
		animalLists.add(TDog.class);
		animalLists.add(TLion.class);
    //注册新类型
		animalLists.add(TCat.class);
	}

如果我们忘记注册了,通过工厂类就无法获取该动物

解决方案(获取基础与IAnimal的所有子类)

我们可以通过Reflections获取继承IAnimal的所有子类,这样我们新增一个动物就无需再进行注册。

如下代码所示:

@Service
public class TAnimalFactory {
	private static List> animalLists = Lists.newArrayList();
	private static Map> animalMaps = Maps.newHashMap();

	static {
    //获取该路径下所有类
		Reflections reflections = new Reflections("com.lanxing.day.daylearning.animal.newT");
    //获取继承了IAnimal的所有类
		Set> classSet = reflections.getSubTypesOf(IAnimal.class);
		animalLists.addAll(classSet);
	}

	@PostConstruct
	public void init() throws IllegalAccessException, InstantiationException {
		for (Class clazz : animalLists){
			IAnimal obj = clazz.newInstance();
			animalMaps.put(obj.getType(), clazz);
		}
	}

	/**
	 * 构建动物类
	 * @param type
	 * @return
	 */
	IAnimal build(int type) throws IllegalAccessException, InstantiationException {
		return animalMaps.get(type).newInstance();
	}
}

我们通过Reflections获取某个路径下的所有类,并通过getSubTypesOf方法获取某个类的所有子类信息。这样,后续我们再增加新的动物,只要保证在原先的路径下,就不需要再手动进行注册。

缺陷

  • Reflections只能扫描某个包下边的所有类,所以所有的子类必须放到同一个路径下

  • getSubTypesOf里的参数必须是所有类都继承的最近层级子类,因为返回的结果里包含了所有该类的子类,不一定是最后一个子类。例如我们还定义了一个抽象类继承IAnimal

    public abstract class AbsAnimal implements IAnimal{
        
        	void beforeTrain(){
        		System.out.println("做准备");
        	}
        }
    

那么通过getSubTypesOf获取的返回结果里也包含AbsAnimal类,但其并没有实现getType接口,所以就会报错。

总结

Reflections是基于Java反射的一个工具类,其提供了很多有意思的方法,有兴趣的同学可以研究下https://static.javadoc.io/org.reflections/reflections/0.9.10/org/reflections/Reflections.html

你可能感兴趣的:(Java,Java,Java子类)