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

303 天前
 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);
    }
}

1152 次点击
所在节点    程序员
19 条回复
JasonLaw
303 天前
Sorry ,putStr(String k, String v)应该为 put(String k, String v),putInt(String k, Integer v)应该为 put(String k, Integer v),我知道通过反射能够解决这个问题。除了反射之外呢?有没有其它优雅一点的方法?
Alphones
303 天前
方法名字没必要区分 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
303 天前
@Alphones 忘了补充上面反射的用法,具体参考如下
Method put = DataContainer.class.getMethod("put", testCase[0].getClass(), testCase[1].getClass());
put.invoke(container,k,v);
JasonLaw
303 天前
@Alphones #2 THX ,不过我在一楼已经说了这种方法了。不管怎样,还是谢啦。
xuanbg
303 天前
相同方法名称,不同方法参数,这叫重载。。。。。代码会根据不同的参数类型自动调用合适的方法。
JasonLaw
303 天前
@xuanbg #5 你可以先详细阅读一下题目,value 的类型是 Object ,你可能会问为什么是 Object ,因为我使用了 TestNG 的 DataProvider 。
Alphones
303 天前
@JasonLaw 不想走反射的话,如果业务量就是那么少,if else 是没问题的,如果后续可能要拓展成多种情况,可以考虑定义一个抽象类和相关的一个入口方法以及钩子方法,以及针对不同类型定义相关的策略对象类,这些策略类可以直接继承上面提到的抽象类并实现钩子方法,然后在抽象类的入口方法里面做一个判断处理最终调用
superychen
303 天前
按照你这个代码,为啥还要区分 String 和 Integer ?直接一个 put(String k, Object v),里面 map.put(k, String.valueOf(v))不可以吗,如果 v 是 String ,调用一次 String.valueOf(v)也没啥问题
superychen
303 天前
```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
303 天前
@superychen #8 因为不单单是 String 和 Integer ,还有可能是 Instant 等类型。
dragondove
303 天前
String val = switch(o) {
case Integer i -> String.valueOf(i);
case String s -> s;
}
superychen
303 天前
@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
303 天前
@superychen #12 不过我不能加一个 put(String key, Object v)。anyway, thank you.
wangYQ
303 天前
可以用个策略模式,用标识不同类型实现不同的策略就可以
zhady009
303 天前
感觉你是需要一个 Jackson 根据 Key+期望的类型对 value 进行转换
sl450282169
303 天前
升级到 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
303 天前
还有预览版的 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
303 天前
通过参数类型去查找方法,但是可能找到的不是正确的方法,下面 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
302 天前
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