简体   繁体   English

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

[英]Java - how do I pass a method as parameter?

I have a stanza of code that is repeatedly used. 我有一个重复使用的代码节。 Here are two examples: 这是两个例子:

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

You can see that these two methods have the general form: 您可以看到这两种方法具有一般形式:

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

Where T is the return type of some set of functions createXXX that have a common signature but different return type. 其中T是某些函数集createXXX的返回类型,它们具有共同的签名但返回类型不同。

I'm pretty sure how I would do this in Javascript, F#, C#, Scala or any number of other languages. 我非常确定如何在Javascript,F#,C#,Scala或其他许多语言中执行此操作。

I just can't seem to wrap my head around how to do this in Java in a type-safe manner that allows me to call each createXXX method so that the setup and tear-down can happen in one block of code, rather than sprinkled throughout each wrapper method. 我似乎无法以类型安全的方式围绕如何在Java中执行此操作,这允许我调用每个createXXX方法,以便设置和拆除可以在一个代码块中发生,而不是撒上贯穿每个包装器方法。

I know I could use reflection, but am looking for a way to do this using lambdas, if possible, or whatever structure makes the most sense in Java. 我知道我可以使用反射,但我正在寻找一种方法来使用lambdas,如果可能的话,或者在Java中最有意义的结构。

Something like this, where the method to be called is passed as the third parameter (I know this code is completely malformed in 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;
}

I've read the apparently duplicate questions: 我已经阅读了明显重复的问题:

  1. How do I pass a method as a parameter in Java 8? 如何在Java 8中将方法作为参数传递?
  2. How do I pass method as a parameter in Java? 如何在Java中将方法作为参数传递?
  3. Java Pass Method as Parameter Java Pass方法作为参数

As well as some questions about method references with generics: 以及有关泛型的方法引用的一些问题:

  1. Java 8 Method reference with generic types 带有泛型类型的Java 8方法引用
  2. Java method reference to a method with generic parameter Java方法引用具有泛型参数的方法

For whatever reason it's not gelling for me, so I'm risking asking a question that will immediately be marked as duplicate in the hopes that someone will take pity and help me out... 无论出于何种原因,它不会给我带来任何影响,所以我冒着问一个会立即被标记为重复的问题,希望有人会怜惜并帮助我...

UPDATE 2015-02-06 更新2015-02-06

While both Louis Wasserman and Neuron gave basically the same answer, it was Louis that more pedantically offered a pattern that worked best for me... with an explicit example of how to pass the method reference . 虽然Louis WassermanNeuron给出了基本相同的答案,但路易斯更迂腐地提供了一种最适合我的模式......以及如何传递方法参考的明确示例。

Here's what ended up working for me: 以下是最终为我工作的内容:

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

Which is called just like Louis suggested: 就像路易斯建议的那样:

public Struct createStruct(
                String typeName,
                Object[] attributes) throws SQLException {
    return createTypeWithCorrectSearchPath(
             typeName,
             attributes,
             delegate()::createStruct
    );
}
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);

The crucial bits are a) creating your own interface type, though I suppose you could use BiFunction<String, Object[]> , b) using method references with :: . 关键的一点是a)创建自己的接口类型,虽然我想你可以使用BiFunction<String, Object[]> ,b)使用带有::方法引用。

You can solve this problem by passing objects which have the sole purpose of performing that one function. 您可以通过传递唯一目的来执行该功能的对象来解决此问题。 Here is a little example to you give you an idea. 这是一个给你一个想法的小例子。 Create an interface for the type of function. 为函数类型创建一个接口。 You need to use generics as I did here: 你需要像我在这里一样使用泛型:

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

Then define your method which takes functions of the type you just specified: 然后定义你的方法,它接受你刚才指定的类型的函数:

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

The most convenient way are anonymous classes where you define the interfaces implementation in line: 最方便的方法是匿名类,您可以在其中定义接口实现:

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..
    }
})

if you can't put the implementation into an anonymous class, because you need to access delegate().createXXX(...) , simply put the definition of the class implementing the interface into the a scope where your method becomes accessible. 如果您不能将实现放入匿名类中,因为您需要访问delegate().createXXX(...) ,只需将实现该接口的类的定义放入您的方法可访问的范围内。

Louis' answer is on the right track; 路易斯的回答是正确的; however, it's not clear to me that delegate() would be available at the point where the create method is called. 但是,我不清楚delegate()是否可以在调用create方法的位置使用。 If it isn't available, then you'll need a three-argument interface method. 如果它不可用,那么你需要一个三参数接口方法。

I don't know what type delegate() returns, but suppose it's Delegate . 我不知道delegate()返回什么类型,但假设它是Delegate One thing to note is that if Delegate has an instance method createArray(String s, Object[] objects) , you can use Delegate::createArray as a method reference for a functional interface for a function with three arguments. 需要注意的一点是,如果Delegate具有实例方法createArray(String s, Object[] objects) ,则可以使用Delegate::createArray作为具有三个参数的函数的功能接口的方法引用。 The first argument would be a Delegate . 第一个参数是一个Delegate Thus: 从而:

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

Now, in your createTypeWithCorrectSearchPath method, you would call the interface like this: 现在,在createTypeWithCorrectSearchPath方法中,您可以像这样调用接口:

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

The first parameter of the call would become the instance on which the two-argument instance method operates. 调用的第一个参数将成为双参数实例方法操作的实例。 That is, if the actual parameter is Delegate::createArray , it would be called like 也就是说,如果实际参数是Delegate::createArray ,则会调用它

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

Unlike in Louis' answer, you have to define your own interface here, because there's no built-in TriFunction class in Java 8. 与Louis的回答不同,你必须在这里定义自己的接口,因为Java 8中没有内置的TriFunction类。

See Section 15.13.3 of the JLS for a complete description of how method references can be used. 有关如何使用方法引用的完整说明,请参见JLS的第15.13.3节 This particular one is listed in the paragraph starting "If the form is ReferenceType :: [TypeArguments] Identifier ". 这个特定的一个列在开头“如果表单是ReferenceType :: [TypeArguments] Identifier ”的段落中

EDIT: After taking another look at the question, I see that createTypeWithCorrectSearchPath was intended to be a private method, and not called from the outside. 编辑:再看看这个问题后,我看到createTypeWithCorrectSearchPath是一个私有方法,而不是从外部调用。 So this answer probably isn't applicable. 所以这个答案可能不适用。 I'm leaving it here, though, because it might be a useful answer in some similar situations. 不过,我将它留在这里,因为在某些类似情况下它可能是一个有用的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM