V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OnlyO
V2EX  ›  Java

Java 有什么优雅的方式动态实例化子类吗

  •  
  •   OnlyO · 2021-11-02 20:29:42 +08:00 · 2651 次点击
    这是一个创建于 876 天前的主题,其中的信息可能已经有所发展或是发生改变。

    场景 class A 有两个子类:

        class A{
            name="A"
        } 
        下面有两个子类  
        class B extend A{
            name="B"
        }
        class C extend A{
            name="C"
        } 
    

    我想通过一个函数能够动态的实例化子类 B 或者 C 伪代码:

     public A getASubClass(String userInputName)
    
        for (A a: A.subclassList){
            if(userInputName == a.name){
                return new a();
            }
        }
        throw new Exception();
    

    想实现的效果是后面只需要添加子类,查找子类的方法就不用修改了

    24 条回复    2021-11-09 09:01:32 +08:00
    leonme
        1
    leonme  
       2021-11-02 20:36:30 +08:00 via iPhone
    子类上加注解,利用反射
    huaouo
        2
    huaouo  
       2021-11-02 20:38:09 +08:00
    Class.forName ?
    OnlyO
        3
    OnlyO  
    OP
       2021-11-02 20:41:13 +08:00
    @huaouo #2 forName 不太好吧,用户输入的就是一个简单的名称
    Vegetable
        4
    Vegetable  
       2021-11-02 20:41:24 +08:00
    恕我冒昧哈,这是不是 JAVA 程序员面试必备设计模式里边的,工厂模式?
    OnlyO
        5
    OnlyO  
    OP
       2021-11-02 20:45:04 +08:00
    @Vegetable #4 工厂模式需要追加 else if 判断吧,我希望查找这个子类的方法不需要修改.
    ikas
        6
    ikas  
       2021-11-02 21:07:33 +08:00
    1.没有实例化呢,怎么取 x.a,如果改成静态,那与注解又有何区别
    2.这种相关类似的代码太多了..无非就是先扫描 /配置类信息,读取标志..然后找到具体类,或者 factory..调用而已
    3.😂
    OnlyO
        7
    OnlyO  
    OP
       2021-11-02 21:15:47 +08:00 via Android
    @ikas 方法多,所以想找个简单的方式。貌似用 spring 的 @component 最简单
    pigspy
        8
    pigspy  
       2021-11-02 21:21:40 +08:00
    有个库叫做 Reflections ,可以做到获取一个类的所有子类
    https://github.com/ronmamo/reflections

    但是我觉得这不是个好做法
    OnlyO
        9
    OnlyO  
    OP
       2021-11-02 21:25:17 +08:00 via Android
    @pigspy 这个类库我写了一半,发现需要硬编码把路径进去,最后没有用
    aguesuka
        10
    aguesuka  
       2021-11-02 21:27:54 +08:00
    标准库有 SPI. 不过 Spring 会灵活很多.
    yidinghe
        11
    yidinghe  
       2021-11-02 21:49:23 +08:00 via Android   ❤️ 2
    反射这块的知识建议完整的看一遍,能解决你当前的问题和将来衍生出来的其他问题。
    dranfree
        12
    dranfree  
       2021-11-03 09:21:55 +08:00
    可以看看 dubbo 的 SPI 做法
    wolfie
        13
    wolfie  
       2021-11-03 16:39:03 +08:00
    工厂扔到 map 里,name 作为 key 。
    seedhk
        14
    seedhk  
       2021-11-03 16:59:00 +08:00
    这不就是工厂模式嘛。。。
    312ybj
        15
    312ybj  
       2021-11-03 18:16:58 +08:00
    向上转型&向下转型, 具体哪个子类,传个标识符
    treeboy
        16
    treeboy  
       2021-11-03 18:23:42 +08:00
    策略模式 + 依赖注入
    night98
        17
    night98  
       2021-11-03 22:41:42 +08:00
    spring 的 applicationcontext.getbean(接口.class)会获取到所有此接口的实现类,然后将其转为一个 map ,之后直接从 map 中 get 就行
    olaloong
        18
    olaloong  
       2021-11-04 09:17:45 +08:00
    如果是 Spring 的话直接 @autowired 到 Map 里,根据别名 get 就是了
    Hayson
        19
    Hayson  
       2021-11-04 13:49:13 +08:00 via Android
    spring 的话
    @autowried
    private Map<string,B> map ,根据 Bean 名 get
    CantSee
        20
    CantSee  
       2021-11-05 11:42:01 +08:00
    private static final Map<String,ActiveContext> activeTypeContainer=new ConcurrentHashMap<>();

    static {
    activeTypeContainer.put(ActiveTpEnum.BUYN_MINUS_ONE.getCode(),new ActiveContext(new ActiveAlgorithmBuyNMinusOne()));
    activeTypeContainer.put(ActiveTpEnum.MCHT_FULL_SUB.getCode(),new ActiveContext(new ActiveAlgorithmFullReduction()));
    activeTypeContainer.put(ActiveTpEnum.MCHT_RANDOM_SUB.getCode(),new ActiveContext(new ActiveAlgorithmRandomSubtraction()));
    }
    CantSee
        21
    CantSee  
       2021-11-05 11:42:44 +08:00
    根据类型获取实例
    OnlyO
        22
    OnlyO  
    OP
       2021-11-05 15:42:11 +08:00
    @CantSee #21 兄弟你给力,不过你这样违法了开闭原则吧,我后期加类型还是要在这里加代码
    我用这样的方式实现了
    ```java
    public Type getTypeService(int type) {
    Reflections reflections = new Reflections("com.xxxx");
    Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(CustomAnnotation.class);
    for (Class<?> aClass : classSet) {
    CustomAnnotation annotation = aClass.getAnnotation(CustomAnnotation.class);
    if (type == CustomAnnotation.type()) {
    try {
    return (Type) aClass.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
    throw new Exc("msg");
    }
    }
    }
    throw new Exc("msg");
    }
    ```
    notwaste
        23
    notwaste  
       2021-11-05 16:46:51 +08:00
    我们的做法是在对应的数据库加一个类似 beanName 的字段,下面初始化的时候判断 beanName 字段是否为空,不为空去 getBean beanName
    CantSee
        24
    CantSee  
       2021-11-09 09:01:32 +08:00
    @OnlyO 用反射还是比较好
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5446 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 08:37 · PVG 16:37 · LAX 01:37 · JFK 04:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.