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

Java - 如果根据参数类型调用不同的方法?

  •  
  •   JasonLaw · 2023-07-12 21:09:43 +08:00 · 1339 次点击
    这是一个创建于 481 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我有以下代码,test case 中的 value 可能是 String 类型,也有可能是 Integer 类型等等。如何根据参数类型调用不同的方法呢?我不想使用 if else 。

    import java.util.HashMap;
    import java.util.Map;
    
    public class Main {
    
        public static void main(String[] args) {
            // test cases
            Object[][] testCases = new Object[][]{
                    {"name", "Martin", "Martin"},
                    {"age", 23, "23"}
            };
    
            for (Object[] testCase : testCases) {
                String k = (String) testCase[0];
                // extract v
                String expectedResult = (String) testCase[2];
                // How can I invoke the correct method without using if else?
                // For value "Martin", I want to invoke putStr, for value 23, I want to invoke putInt
            }
        }
    }
    
    class DataContainer {
    
        private final Map<String, String> map = new HashMap<>();
    
        public void putStr(String k, String v) {
            map.put(k, v);
        }
    
        public void putInt(String k, Integer v) {
            map.put(k, String.valueOf(v));
        }
    
        public String get(String k) {
            return map.get(k);
        }
    }
    
    
    19 条回复    2023-07-13 17:08:36 +08:00
    JasonLaw
        1
    JasonLaw  
    OP
       2023-07-12 21:27:34 +08:00
    Sorry ,putStr(String k, String v)应该为 put(String k, String v),putInt(String k, Integer v)应该为 put(String k, Integer v),我知道通过反射能够解决这个问题。除了反射之外呢?有没有其它优雅一点的方法?
    Alphones
        2
    Alphones  
       2023-07-13 00:06:42 +08:00
    方法名字没必要区分 putStr 或者 putInt ,都叫 put

    public void put(String k, String v) {
    map.put(k, v);
    }

    public void put(String k, Integer v) {
    this.put(k,String.valueOf(v));
    }
    Alphones
        3
    Alphones  
       2023-07-13 00:13:35 +08:00
    @Alphones 忘了补充上面反射的用法,具体参考如下
    Method put = DataContainer.class.getMethod("put", testCase[0].getClass(), testCase[1].getClass());
    put.invoke(container,k,v);
    JasonLaw
        4
    JasonLaw  
    OP
       2023-07-13 08:05:23 +08:00 via iPhone
    @Alphones #2 THX ,不过我在一楼已经说了这种方法了。不管怎样,还是谢啦。
    xuanbg
        5
    xuanbg  
       2023-07-13 08:07:18 +08:00
    相同方法名称,不同方法参数,这叫重载。。。。。代码会根据不同的参数类型自动调用合适的方法。
    JasonLaw
        6
    JasonLaw  
    OP
       2023-07-13 08:42:07 +08:00 via iPhone
    @xuanbg #5 你可以先详细阅读一下题目,value 的类型是 Object ,你可能会问为什么是 Object ,因为我使用了 TestNG 的 DataProvider 。
    Alphones
        7
    Alphones  
       2023-07-13 09:22:06 +08:00
    @JasonLaw 不想走反射的话,如果业务量就是那么少,if else 是没问题的,如果后续可能要拓展成多种情况,可以考虑定义一个抽象类和相关的一个入口方法以及钩子方法,以及针对不同类型定义相关的策略对象类,这些策略类可以直接继承上面提到的抽象类并实现钩子方法,然后在抽象类的入口方法里面做一个判断处理最终调用
    superychen
        8
    superychen  
       2023-07-13 09:24:37 +08:00
    按照你这个代码,为啥还要区分 String 和 Integer ?直接一个 put(String k, Object v),里面 map.put(k, String.valueOf(v))不可以吗,如果 v 是 String ,调用一次 String.valueOf(v)也没啥问题
    superychen
        9
    superychen  
       2023-07-13 09:27:14 +08:00
    ```java
    class DataContainer {

    private final Map<String, String> map = new HashMap<>();

    public void put(String key, Object v) {
    map.put(key, null == v ? null : String.valueOf(v));
    }

    public String get(String k) {
    return map.get(k);
    }

    }
    ```
    JasonLaw
        10
    JasonLaw  
    OP
       2023-07-13 09:39:36 +08:00 via iPhone
    @superychen #8 因为不单单是 String 和 Integer ,还有可能是 Instant 等类型。
    dragondove
        11
    dragondove  
       2023-07-13 09:49:41 +08:00
    String val = switch(o) {
    case Integer i -> String.valueOf(i);
    case String s -> s;
    }
    superychen
        12
    superychen  
       2023-07-13 10:16:40 +08:00
    @JasonLaw 那感觉只能为每个 class 类型指定 toString 方法,最后根据 class 类型直接找对应方法进行转换


    private static final Map<Class<?>, Function<Object, String>> FUNCTIONS = Map.of(
    String.class, String::valueOf,
    Integer.class, String::valueOf,
    Instant.class, Object::toString
    );

    public void put(String key, Object v) {
    map.put(key, null == v ? null : FUNCTIONS.get(v.getClass()).apply(v));
    }
    JasonLaw
        13
    JasonLaw  
    OP
       2023-07-13 10:33:09 +08:00 via iPhone
    @superychen #12 不过我不能加一个 put(String key, Object v)。anyway, thank you.
    wangYQ
        14
    wangYQ  
       2023-07-13 11:15:19 +08:00
    可以用个策略模式,用标识不同类型实现不同的策略就可以
    zhady009
        15
    zhady009  
       2023-07-13 12:37:12 +08:00
    感觉你是需要一个 Jackson 根据 Key+期望的类型对 value 进行转换
    sl450282169
        16
    sl450282169  
       2023-07-13 13:39:24 +08:00
    升级到 jdk17 使用模式匹配即可

    public void process(Object obj) {
    if (obj instanceof String s) {
    System.out.println("String value: " + s);
    } else if (obj instanceof Integer i) {
    System.out.println("Integer value: " + i);
    } else if (obj instanceof Boolean b) {
    System.out.println("Boolean value: " + b);
    } else {
    System.out.println("Unexpected type");
    }
    }
    sl450282169
        17
    sl450282169  
       2023-07-13 13:44:53 +08:00
    还有预览版的 switch

    public void process(Object obj) {
    switch (obj) {
    case String s -> System.out.println("String value: " + s);
    case Integer i -> System.out.println("Integer value: " + i);
    case Boolean b -> System.out.println("Boolean value: " + b);
    default -> System.out.println("Unexpected type");
    }
    }
    montaro2017
        18
    montaro2017  
       2023-07-13 16:08:06 +08:00
    通过参数类型去查找方法,但是可能找到的不是正确的方法,下面 DataContainer 中的方法最好用重载写
    ```java
    public class MatchMethod {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
    // test cases
    Object[][] testCases = new Object[][]{
    {"name", "Martin", "Martin"},
    {"age", 23, "23"},
    {"age", "23"}
    };
    DataContainer instance = new DataContainer();
    Method[] methods = DataContainer.class.getMethods();

    for (Object[] testCase : testCases) {
    Class<?>[] methodParameterTypes = Arrays.stream(testCase).map(Object::getClass).toArray(Class[]::new);
    Method method = findMethod(methods, methodParameterTypes);
    if (method != null) {
    System.out.println("method = " + method.toGenericString());
    method.invoke(instance, testCase);
    } else {
    System.out.println("method is null");
    }
    }
    }

    private static Method findMethod(Method[] methods, Class<?>[] methodParameterTypes) {
    for (Method method : methods) {
    Class<?>[] parameterTypes = method.getParameterTypes();
    if (Arrays.equals(parameterTypes, methodParameterTypes)) {
    return method;
    }
    }
    return null;
    }
    }

    ```
    输出
    ```
    method is null
    method is null
    method = public void DataContainer.putStr(java.lang.String,java.lang.String)
    ```
    wolfie
        19
    wolfie  
       2023-07-13 17:08:36 +08:00
    putStr 、putInt 参数列表 类型不同,不然可以用 FunctionalInterface 。

    ---

    你这个最好弄成,ValueDecorator 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5339 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 51ms · UTC 09:18 · PVG 17:18 · LAX 01:18 · JFK 04:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.