使用方法引用而不是多参数lambda表达式

23

我对“特定类型的任意对象的实例方法引用”的概念感到困惑。Oracle的文档提供了一个相关示例:

String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

我看到的这种方法引用的大多数示例都是像这样的:如果lambda表达式是这样的:x -> x.func(),那么您可以将其写成 ClassOfX::func。文档中的示例说:

方法引用String::compareToIgnoreCase的等效lambda表达式将具有形式参数列表(String a,String b),其中a和b是任意名称,用于更好地描述此示例。方法引用将调用方法a.compareToIgnoreCase(b)。

问题是:对于任何两个参数的lambda,例如(a,b) -> a.func(b)func方法必须是lambda的第一个参数的实例方法,并且lambda的第二个参数将作为参数传递给该方法吗?如果我们有多个参数的lambda,那么func方法必须是lambda的第一个参数的实例方法,并且lambda的其他参数将按照它们在lambda中出现的顺序传递给func吗?我的意思是,我们可以将(a,b,c) -> a.func(b,c)写成ClassOfA:: func

抱歉我的英语不好,我希望我已经清楚地说明了问题。

4个回答

33

SomeClass::func 的含义取决于 func 是静态方法还是实例方法。

(1) 如果 func 是静态方法,则 SomeClass::func 是一个 lambda 表达式,该表达式只是将所有参数传递给该方法:

(a, b, c) -> SomeClass.func(a, b, c);

(2) 如果 func 是一个实例方法,那么 SomeClass::func 就是一个使用第一个参数作为实例的 lambda 表达式,就像你所想的那样:

(a, b, c) -> a.func(b, c);

其中a的类型为SomeClass

编辑:Sotirios的答案展示了另一种不同类型的方法引用:example::method,其中example是一个引用变量(而不是一个类名)。这意味着相同的含义。

(a, b) -> example.method(a, b);

或者更准确地说

(a, b) -> __someFinalTemporary.method(a, b);

在方法引用被评估的点,__someFinalTemporary 被赋值给 example,因此如果 example 后来发生了变化,该方法仍将使用 example 的早期值。

[第四种是将参数传递给构造函数的 SomeClass::new,我想那就是全部了。]


13

以下是一个小例子,演示了实例方法引用的行为。

public class Example {  
    public static void main(String[] args) throws Exception {
        List<String> strings = new ArrayList<String>();
        Example example = new Example();
        Functional methodRef = example::method;
        methodRef.funct("a string", strings);
        System.out.println("List now contains: " + strings);

        Functional lambda = (String s, List<String> l) -> {
            example.method(s, l);
        };
        lambda.funct("another string", strings);
        System.out.println("List now contains: " + strings);
    }

    interface Functional {
        void funct(String value, List<String> list);
    }

    void method(String value, List<String> toAddTo) {
        System.out.println("adding");
        toAddTo.add(value);
    }
}

它打印

adding
List now contains: [a string]
adding
List now contains: [a string, another string]

这两者或多或少是等价的。在lambda的情况下,被调用方法的变量需要有效地是final


它看起来有点像魔法。如果Example没有实现Functional,为什么Functional methodRef = example::method;会起作用呢?它确实可以工作,但我真的不太明白为什么。 - Paulo Guedes
1
@PauloGuedes 方法引用被转换为实现“Functional”的内容。在这种情况下,传递给“func”的两个参数将被传播到“example”上调用的“method”中。 - Sotirios Delimanolis

7
我是一名有用的助手,可以为您翻译以下内容:

我的意思是,我们可以写成ClassOfA::func,而不是(a, b, c) -> a.func(b, c)

是的,这是正确的。


5
根据Joshua Bloch在他的"Effective Java"(第3版)中所述,方法引用有五种用例。 enter image description here

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