Java - 如何将方法作为参数传递?

时间:2016-02-06 19:54:29

标签: java lambda

我有一个重复使用的代码节。以下是两个例子:

public java.sql.Struct createStruct(String typeName, Object[] attributes) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  Struct ret = delegate().createStruct(name.toLowerCase(), attributes);
  setSearchPath(searchPath);
  return ret;
}

public java.sql.Array createArray(String typeName, Object[] elements) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  Array ret = delegate().createArray(name.toLowerCase(), elements);
  setSearchPath(searchPath);
  return ret;
}

您可以看到这两种方法具有一般形式:

public <T> createXXX(String typeName, Object[] objects) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  T ret = delegate().createXXX(name.toLowerCase(), objects);
  setSearchPath(searchPath);
  return ret;
}

其中T是某些函数createXXX的返回类型,它们具有共同的签名但返回类型不同。

我非常确定如何在Javascript,F#,C#,Scala或其他许多语言中执行此操作。

我似乎无法以类型安全的方式解决如何在Java中执行此操作,这样我就可以调用每个createXXX方法,以便在一个方法中进行设置和拆除代码块,而不是遍及每个包装器方法。

我知道我可以使用反射,但我正在寻找一种方法来使用lambdas,如果可能的话,或者在Java中最有意义的结构。

像这样的东西,其中要调用的方法作为第三个参数传递(我知道这个代码在Java中完全格式错误):

public Struct createStruct(String typeName, Object[] attributes) {
  return createTypeWithCorrectSearchPath<>(
           typeName, 
           attributes, 
           delegate().createStruct
  );
}

public Struct createArray(String typeName, Object[] elements) {
  return createTypeWithCorrectSearchPath<>(
           typeName, 
           elements, 
           delegate().createArray
  );
}

private <T> T createTypeWithCorrectSearchPath(
                String typeName, 
                Object[] objects, 
                [SOME-METHOD-HERE]) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  T ret = [SOME-METHOD-HERE](name, objects);
  setSearchPath(searchPath);
  return ret;
}

我已经阅读了明显重复的问题:

  1. How do I pass a method as a parameter in Java 8?
  2. How do I pass method as a parameter in Java?
  3. Java Pass Method as Parameter
  4. 以及有关使用泛型的方法引用的一些问题:

    1. Java 8 Method reference with generic types
    2. Java method reference to a method with generic parameter
    3. 无论出于何种原因,这对我来说都没有凝胶化,所以我冒着问一个会立即被标记为重复的问题,希望有人会怜惜并帮助我......

      更新2015-02-06

      虽然Louis WassermanNeuron都给出了基本相同的答案,但路易斯更迂腐地提供了一种最适合我的模式......以及如何传递方法参考。

      以下是最终为我工作的内容:

      interface SearchPathCreator<T> {
        T create(String typeName, Object[] objects) throws SQLException;
      }
      
      private <T> T createTypeWithCorrectSearchPath(
                      String typeName,
                      Object[] objects,
                      SearchPathCreator<?> creator) throws SQLException {
        String searchPath = getSearchPath();
        String name = setSearchPathToSchema(typeName);
        //noinspection unchecked
        T ret = (T) creator.create(name.toLowerCase(), objects);
        setSearchPath(searchPath);
        return ret;
      }
      

      这就像路易斯建议的那样:

      public Struct createStruct(
                      String typeName,
                      Object[] attributes) throws SQLException {
          return createTypeWithCorrectSearchPath(
                   typeName,
                   attributes,
                   delegate()::createStruct
          );
      }
      

3 个答案:

答案 0 :(得分:5)

interface MyMethodType<T> {
  T method(String name, Object[] objects);
}


private <T> T createTypeWithCorrectSearchPath(
            String typeName, 
            Object[] objects, 
            MyMethodType<T> impl) {
    String searchPath = getSearchPath();
    String name = setSearchPathToSchema(typeName);
    T ret = impl.method(name, objects);
    setSearchPath(searchPath);
    return ret;
}

createTypeWithCorrectSearchPath(typeName, objects, delegate()::createStruct);

关键部分是a)创建自己的接口类型,但我想你可以使用BiFunction<String, Object[]>,b)使用::的方法引用。

答案 1 :(得分:2)

您可以通过传递唯一目的来执行该功能的对象来解决此问题。这是一个给你一个想法的小例子。 为函数类型创建一个接口。你需要像我在这里一样使用泛型:

public interface Creator<A>{
    A create(String name, Object[] attributes);
}

然后定义你的方法,它接受你刚才指定的类型的函数:

public <A> A create(String typeName, Object[] attributes, Creator<A> creator){
    String searchPath = getSearchPath();
    String name = setSearchPathToSchema(typeName);
    A ret = creator.create(name.toLowerCase(), attributes);
    setSearchPath(searchPath);
    return ret;
}

最方便的方法是在线定义接口实现的匿名类:

java.sql.Struct struct = create("foo bar", new Object[]{"att1", "att2"}, new Creator<Struct>() {
    @Override
    public Struct create(String name, Object[] attributes) {
        return //your implementation..
    }
})

如果您不能将实现放入匿名类中,因为您需要访问delegate().createXXX(...),只需将实现该接口的类的定义放入您的方法可访问的范围内。

答案 2 :(得分:2)

路易斯的回答是正确的;但是,我不清楚调用create方法时delegate()是否可用。如果它不可用,那么你需要一个三参数接口方法。

我不知道delegate()返回什么类型,但假设它是Delegate。需要注意的一点是,如果Delegate具有实例方法createArray(String s, Object[] objects),则可以使用Delegate::createArray作为 3 的函数的功能接口的方法参考参数。第一个参数是Delegate。因此:

interface MyMethodType<T> {
    T method(Delegate delegate, String name, Object[] objects);
}

现在,在您的createTypeWithCorrectSearchPath方法中,您可以像这样调用界面:

impl.method(delegate(), name.toLowerCase(), objects);

调用的第一个参数将成为双参数实例方法运行的实例。也就是说,如果实际参数是Delegate::createArray,它将被称为

delegate().createArray(name.toLowerCase(), objects);

与路易斯的答案不同,你必须在这里定义自己的界面,因为Java 8中没有内置的TriFunction类。

有关如何使用方法引用的完整说明,请参阅Section 15.13.3 of the JLS。这个特定的列表在“如果表格是 ReferenceType :: [TypeArguments] Identifier ”开头的段落中列出。

编辑:再看一下这个问题,我发现createTypeWithCorrectSearchPath是私有方法,而不是从外面调用。所以这个答案可能不适用。不过,我将它留在这里,因为在某些类似情况下它可能是一个有用的答案。