【问题标题】:Java - how do I pass a method as parameter?Java - 如何将方法作为参数传递?
【发布时间】:2016-02-06 19:54:29
【问题描述】:

我有一段重复使用的代码。这里有两个例子:

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

还有一些关于方法引用与泛型的问题:

  1. Java 8 Method reference with generic types
  2. Java method reference to a method with generic parameter

无论出于何种原因,它对我来说都不是很好,所以我冒着风险提出一个会立即被标记为重复的问题,希望有人会同情并帮助我......

2015-02-06 更新

虽然Louis WassermanNeuron 给出了基本相同的答案,但Louis 更迂腐地提供了一种最适合我的模式......并提供了一个关于如何传递方法参考的明确示例。

这就是最终对我有用的东西:

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;
}

就像 Louis 建议的那样:

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

【问题讨论】:

    标签: java lambda


    【解决方案1】:
    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&lt;String, Object[]&gt;,b) 使用带有 :: 的方法引用。

    【讨论】:

    • @ajb OP 的问题似乎表明传入delegate()::createStruct 没问题,在“类似这样的东西”部分下方。我的回答是基于这个假设。如果 OP 没有那个例子,我会把 Delegate 移到接口方法中。
    • 这个答案让我走得最远。当我逐字使用此模式时,createTypeWithCorrectSearchPath 中的方法引用delegate()::createStructBad return type in method reference: cannot convert java.sql.Struct` 显示为T。我已经用最终工作的结构更新了我的问题。
    • 这看起来……真的很奇怪。在我看来,您更可能想要一个显式类型参数,而不是您似乎最终得到的未经检查的强制转换。
    • 由于这个搜索路径的东西似乎是关于恢复以前的状态,我宁愿使用String searchPath = getSearchPath(); try { return impl.method( setSearchPathToSchema(typeName), objects); } finally { setSearchPath(searchPath); }
    • @LouisWasserman 我更喜欢显式类型参数,但我最终得到的是编译器所要求的。我猜编译器无法确定 T 在编译时应该是什么(在这种情况下它可能是 StructArray),因此需要使用通配符。但这只是我的猜测......
    【解决方案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(...),只需将实现接口的类的定义放到你的方法可以访问的范围内。

    【讨论】:

      【解决方案3】:

      Louis 的答案是正确的;但是,我不清楚 delegate() 在调用 create 方法时是否可用。如果它不可用,那么您将需要一个三参数接口方法。

      我不知道delegate() 返回什么类型,但假设它是Delegate。需要注意的一点是,如果Delegate 有一个实例方法createArray(String s, Object[] objects),您可以使用Delegate::createArray 作为具有三个 参数的函数的函数接口的方法引用。第一个参数是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);
      

      与 Louis 的回答不同,您必须在此处定义自己的接口,因为 Java 8 中没有内置的 TriFunction 类。

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

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

      【讨论】:

      • 虽然这不是针对我当前的问题,但它非常有帮助。
      猜你喜欢
      • 1970-01-01
      • 2022-01-22
      • 2011-10-14
      • 1970-01-01
      • 2012-07-05
      • 2017-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多