如何在Java中将一个字符串映射到一个函数?

14

目前,我有许多实现Processor接口的Java类,这意味着它们都有一个processRequest(String key)方法。想法是每个类都有一些(比如说,<10)成员Strings,每个字符串通过processRequest方法映射到该类中的一个方法,就像这样:

class FooProcessor implements Processor
{
    String key1 = "abc";
    String key2 = "def";
    String key3 = "ghi";
    // and so on...

    String processRequest(String key)
    {
        String toReturn = null;
        if (key1.equals(key)) toReturn = method1();
        else if (key2.equals(key)) toReturn = method2();
        else if (key3.equals(key)) toReturn = method3();
        // and so on...

        return toReturn;
    }

    String method1() { // do stuff }
    String method2() { // do other stuff }
    String method3() { // do other other stuff }
    // and so on...
}
你需要一个可以在运行时访问的从键到函数的映射,你已经有了一个知道所有键的管理器,但不知道键和函数之间的映射关系。你的第一反应是使用带有 if-else 语句的映射,但是你需要一个支持一等公民函数的语言才能这样做,所以你考虑使用一个 `Map`。你也想过通过将键放入数组中并使用反射来调用方法,但你认为这可能会减慢速度。你正在寻找一种干净、开销最小的方式来将字符串映射到某种函数对象,并调用类似于 `getReturnType()` 的函数。如果需要,你可以使用第三方库,但最好避免每次执行方法查找时都要使用反射 - 也许可以使用将 `Map` 与反射相结合的缓存策略。

2
你不能够使用String to Method (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html)吗?这样你就可以缓存需要执行的方法了。 - Chris Dennett
@Chris - 是的,基本上这就是我在问题末尾所要表达的意思。 - Matt Ball
8个回答

8

虽然没有一流的独立函数,但是您可以使用接口来实现所需功能。创建一个代表您函数的接口。例如,您可以创建以下内容:

public interface ComputeString
{
    public String invoke();
}

那么您可以像一开始想的那样创建一个Map<String,ComputeString>对象。使用映射比反射更快,并且还提供更多类型安全性,因此我建议使用上述方法。


3
这意味着我需要为每个想要调用的函数编写一个实现类,这看起来很笨重,而且每个函数都需要一些样板代码。对我来说听起来并不那么好。 - Matt Ball
1
它非常笨重,但这是Java的问题,因为它不直接支持高阶函数。 - Chris Conway
@Bears,是的,代码看起来有点笨重,但你可以构建匿名类(anonymous classes),这应该会使它不那么笨重,比如 map.put(str,new ComputeString(){public String invoke(){ return "blah"; }}); - Michael Aaron Safyan

4

虽然你不能拥有一流的函数,但是可以基于接口创建匿名类:

interface ProcessingMethod {
   String method();
}

Map<String, ProcessingMethod> methodMap = new HashMap<String, ProcessingMethod>();
methodMap.put("abc", new ProcessingMethod() {
   String method() { return "xyz" }
});
methodMap.put("def", new ProcessingMethod() {
   String method() { return "uvw" }
});

methodMap.get("abc").method();

或者您可以使用Scala :-)


1

这个示例使用了一个命名函数的enum和一个抽象的FunctionAdapter来调用具有可变数量同类参数的函数,而不需要反射。 lookup()函数只是简单地使用Enum.valueOf,但对于大量函数来说,使用Map可能更值得。


1

你不能把 String 转换成 Method 吗?这样你就可以缓存需要执行的方法了。


0
public class CarDetailsService {

    private final CarRepository carRepository;

    private final Map<String, Function<CarDTO, String>> carColumnMapper = new HashMap<>();

    public ApplicationDetailsServiceImpl(CarRepository carRepository) {
        this.carRepository = carRepository;

       //---- Initialise all the mappings ------- //

        carColumnMapper.put("BRAND", CarDTO::getBrandName);
        carColumnMapper.put("MILEAGE", CarDTO::getMileage);
    }

    public Map<String, List<CarDTO>> getListOfCars(String groupBy) {

        return carRepository.findAll()
                .stream()
                .map(toCarDTO)
                .collect(groupingBy(carColumnMapper.get(groupBy.toUpperCase())));
    }

    Function<CarDetails, CarDTO> toCarDTO = (carDetails) -> CarDTO
            .builder()
            .brand(carDetails.getBrand())
            .engineCapacity(carDetails.getEngineCapacity())
            .mileage(carDetails.getMileage())
            .fuel(carDetails.getFuel())
            .price(carDetails.getPrice())
            .build();
}

0
使用Map,其中键是字符串,值是实现包含method()方法的接口的对象。这样,您可以从地图中获取包含所需方法的对象。然后只需在对象上调用该方法即可。例如:
class FooProcessor implements Processor{

    Map<String, FooMethod> myMap;

    String processRequest(String key){
        FooMethod aMethod = myMap.getValue(key);
        return aMethod.method();
    }        
}

0

正如您所注意到的,您可以使用反射API来实现您想要的功能,但是您会失去Java编译器的一些好处,除了您已经遇到的问题。将您的字符串包装在一个对象中并使用访问者模式是否可以解决您的问题?每个StringWrapper只接受具有正确方法的Visitor,或类似这样的东西。


我认为访问者模式在这里不是正确的方法 - 也就是说,我对它并不满意。Visitor将操作什么?操作会是什么? - Matt Ball
@熊会吃掉你 - 当我考虑这个想法时,我想象访问者会访问每个字符串的包装器,但是又看起来你不知道什么时候会出现哪个字符串。我认为接口的想法更简单。 - justkt
3
访问者?请使用命令模式或策略模式。其他答案演示了命令模式。您可以在这里找到设计模式的真实生活例子(https://dev59.com/vXI-5IYBdhLWcg3wy7sd#2707195),以便更好地学习它们。 - BalusC

0

关于反射 API 中的 Method 类怎么样?您可以根据名称、参数或返回类型查找类的方法。然后,只需调用 Method.invoke(this, parameters)。

这与您所谈论的 JavaScript 中的 Map 差不多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接