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

2023-07-12 21:09:43 +08:00
 JasonLaw

我有以下代码,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);
    }
}

1351 次点击
所在节点    程序员
19 条回复
JasonLaw
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
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
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
2023-07-13 08:05:23 +08:00
@Alphones #2 THX ,不过我在一楼已经说了这种方法了。不管怎样,还是谢啦。
xuanbg
2023-07-13 08:07:18 +08:00
相同方法名称,不同方法参数,这叫重载。。。。。代码会根据不同的参数类型自动调用合适的方法。
JasonLaw
2023-07-13 08:42:07 +08:00
@xuanbg #5 你可以先详细阅读一下题目,value 的类型是 Object ,你可能会问为什么是 Object ,因为我使用了 TestNG 的 DataProvider 。
Alphones
2023-07-13 09:22:06 +08:00
@JasonLaw 不想走反射的话,如果业务量就是那么少,if else 是没问题的,如果后续可能要拓展成多种情况,可以考虑定义一个抽象类和相关的一个入口方法以及钩子方法,以及针对不同类型定义相关的策略对象类,这些策略类可以直接继承上面提到的抽象类并实现钩子方法,然后在抽象类的入口方法里面做一个判断处理最终调用
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
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
2023-07-13 09:39:36 +08:00
@superychen #8 因为不单单是 String 和 Integer ,还有可能是 Instant 等类型。
dragondove
2023-07-13 09:49:41 +08:00
String val = switch(o) {
case Integer i -> String.valueOf(i);
case String s -> s;
}
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
2023-07-13 10:33:09 +08:00
@superychen #12 不过我不能加一个 put(String key, Object v)。anyway, thank you.
wangYQ
2023-07-13 11:15:19 +08:00
可以用个策略模式,用标识不同类型实现不同的策略就可以
zhady009
2023-07-13 12:37:12 +08:00
感觉你是需要一个 Jackson 根据 Key+期望的类型对 value 进行转换
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
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
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
2023-07-13 17:08:36 +08:00
putStr 、putInt 参数列表 类型不同,不然可以用 FunctionalInterface 。

---

你这个最好弄成,ValueDecorator 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/956277

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX