获取某个类的子类(项目实战(要不就用SpringCotext+反射))

在我们写工厂类的时候,可能会根据不同的类型(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<Class<? extends IAnimal>> animalLists = Lists.newArrayList();
    private static Map<Integer, Class<? extends IAnimal>> animalMaps = Maps.newHashMap();

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

    @PostConstruct
    public void init() throws IllegalAccessException, InstantiationException {
        for (Class<? extends IAnimal> 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<Class<? extends IAnimal>> animalLists = Lists.newArrayList();
    private static Map<Integer, Class<? extends IAnimal>> animalMaps = Maps.newHashMap();

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

    @PostConstruct
    public void init() throws IllegalAccessException, InstantiationException {
        for (Class<? extends IAnimal> 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接口,所以就会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值