方法引用,双冒号(::)操作符

方法引用,双冒号(::)操作符

在Java8中,双冒号操作符称为方法引用。下面这个例子中,匿名类打印List

List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(new Consumer<String>() {       // anonymous class
    @Override
    public void accept(String str) {
        System.out.println(str);
    }
});

匿名类 -> Lambda表达式

List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(str -> System.out.println(str)); // lambda

Lambda表达式 -> 方法引用

List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(System.out::println);          // method references

匿名类 -> Lambda表达式 -> 方法引用

Lambda表达式和方法引用其实并没有额外的作用,仅仅只是调用已存在的方法。使用方法引用,代码可读性更好。

下面是四种形式的方法引用

  1. 引用一个静态方法ClassName::staticMethodName
  2. 引用一个指定对象的实例方法Object::instanceMethodName
  3. 引用指定类型的任意对象的实例方法ContainingType::methodName
  4. 引用一个构造函数ClassName::new

静态方法

Lambda表达式

(args) -> ClassName.staticMethodName(args)

方法引用

ClassName::staticMethodName

下面例子打印String的列表,方法引用一个静态方法SimplePrinter::print

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Java8MethodReference1a {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("A", "B", "C");

        // anonymous class
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String x) {
                SimplePrinter.print(x);
            }
        });

        // lambda expression
        list.forEach(x -> SimplePrinter.print(x));

        // method reference
        list.forEach(SimplePrinter::print);

    }

}

class SimplePrinter {
    public static void print(String str) {
        System.out.println(str);
    }
}

下面这个例子,转换一个String的列表为Integer的列表,方法引用静态方法Integer::parseInt

Integer.java

public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
  }

MethodReference.java

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Java8MethodReference1b {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("1", "2", "3");

        // anonymous class
        List<Integer> collect1 = list.stream()
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) {
                        return Integer.parseInt(s);
                    }
                })
                .collect(Collectors.toList());

        // lambda expression
        List<Integer> collect2 = list.stream()
                .map(s -> Integer.parseInt(s))
                .collect(Collectors.toList());

        // method reference
        List<Integer> collect3 = list.stream()
                .map(Integer::parseInt)
                .collect(Collectors.toList());
    }
}

下面这个例子,进行两个Integer相加并返回String结果。通过一个方法引用静态方法IntegerUtils::join作为参数传递给另一个接收BiFunction的方法。

import java.util.function.BiFunction;

public class Java8MethodReference1c {

    public static void main(String[] args) {

        // anonymous class
        String result1 = playTwoArgument(1, 2, new BiFunction<Integer, Integer, String>() {
                  @Override
                  public String apply(Integer a, Integer b) {
                      return IntegerUtils.join(a, b);
                  }
              });                                                                   // 3

        // lambda
        String result1 = playTwoArgument(1, 2, (a, b) -> IntegerUtils.join(a, b));  // 3

        // method reference
        String result2 = playTwoArgument(1, 2, IntegerUtils::join);                 // 3

    }

    private static <R> R playTwoArgument(Integer i1, Integer i2,
        BiFunction<Integer, Integer, R> func) {
        return func.apply(i1, i2);
    }

}

class IntegerUtils{

    public static String join(Integer a, Integer b) {
        return String.valueOf(a + b);
    }

}

引用指定对象的实例方法

lambda表达式

(args) -> object.instanceMethodName(args)

方法引用

object::instanceMethodName

下面例子,通过Salary字段给Employee对象排序。通过引用ComparatorProvider对象的实例方法compareBySalary来实现。

MethodReference.java

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

public class Java8MethodReference2 {

    public static void main(String[] args) {

        List<Employee> list = Arrays.asList(
                new Employee("mkyong", 38, BigDecimal.valueOf(3800)),
                new Employee("zilap", 5, BigDecimal.valueOf(100)),
                new Employee("ali", 25, BigDecimal.valueOf(2500)),
                new Employee("unknown", 99, BigDecimal.valueOf(9999)));

        // anonymous class
        /*list.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return provider.compareBySalary(o1, o2);
            }
        });*/

        ComparatorProvider provider = new ComparatorProvider();

        // lambda
        // list.sort((o1, o2) -> provider.compareBySalary(o1, o2));

        // method reference
        list.sort(provider::compareBySalary);

        list.forEach(x -> System.out.println(x));

    }

}

class ComparatorProvider {

    public int compareByAge(Employee o1, Employee o2) {
        return o1.getAge().compareTo(o2.getAge());
    }

    public int compareByName(Employee o1, Employee o2) {
        return o1.getName().compareTo(o2.getName());
    }

    public int compareBySalary(Employee o1, Employee o2) {
        return o1.getAge().compareTo(o2.getAge());
    }

}

Employee.java

import java.math.BigDecimal;

public class Employee {

    String name;
    Integer age;
    BigDecimal salary;

    // generated by IDE, getters, setters, constructor, toString
}

输出

Employee{name='zilap', age=5, salary=100}
Employee{name='ali', age=25, salary=2500}
Employee{name='mkyong', age=38, salary=3800}
Employee{name='unknown', age=99, salary=9999}

引用指定类型任意对象的实例方法

这个描述有点迷惑,需要进行一点解释,看下面的例子

Lambda表达式

// arg0 is the first argument
(arg0, rest_of_args) -> arg0.methodName(rest_of_args)

// example, assume a and b are String
(a, b) -> a.compareToIgnoreCase(b)

方法引用

// first argument type
arg0_Type::methodName

// arg0 is type of ClassName
ClassName::methodName

// example, a is type of String
String::compareToIgnoreCase

(String a, String b),a和b是任意的名字,String是任意的类型。这个例子展示了方法引用指定String类型的任意对象a的实例方法compareToIgnoreCase

查看下官方的方法引用示例

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

我们传递了一个方法引用String::compareToIgnoreCase作为Arrays.sort的构造器

查看Arrays.sort的方法签名

public static <T> void sort(T[] a, Comparator<? super T> c) {
}

在上面示例中,Arrays.sort需要一个Comparator的构造器。这个Comparator构造器是一个方法接口。compare抽象方法需要匹配BiFunction<String, String, Integer>,它需要两个String类型的参数,然后返回int类型。

Comparator.java

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);  // this matches BiFunction<String, String, Integer>
}

查看BiFunction的方法签名

BiFunction.java

@FunctionalInterface
public interface BiFunction<T, U, R> {
      R apply(T t, U u);
}

下面的Lambda是rBiFunction<String, String, Integer>的实现。所以Arrays.sort接收下面的Lambda表达式。

(String a, String b) -> a.compareToIgnoreCase(b) // return int

// a is type of String
// method reference
String::compareToIgnoreCase

接下来看另一个示例

import java.util.function.BiPredicate;
import java.util.function.Function;

public class Java8MethodReference3a {

    public static void main(String[] args) {

        // lambda
        int result = playOneArgument("mkyong", x -> x.length());   // 6

        // method reference
        int result2 = playOneArgument("mkyong", String::length);   // 6

        // lambda
        Boolean result3 = playTwoArgument("mkyong", "y", (a, b) -> a.contains(b)); // true

        // method reference
        Boolean result4 = playTwoArgument("mkyong", "y", String::contains);        // true

        // lambda
        Boolean result5 = playTwoArgument("mkyong", "1", (a, b) -> a.startsWith(b)); // false

        // method reference
        Boolean result6 = playTwoArgument("mkyong", "y", String::startsWith);        // false

        System.out.println(result6);
    }

    static <R> R playOneArgument(String s1, Function<String, R> func) {
        return func.apply(s1);
    }

    static Boolean playTwoArgument(String s1, String s2, BiPredicate<String, String> func) {
        return func.test(s1, s2);
    }

}

让我们来看另外一个示例,自定义对象

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.function.BiFunction;

public class Java8MethodReference3b {

    public static void main(String[] args) {

        Invoice obj = new Invoice("A001", BigDecimal.valueOf(1.99), 3);

        InvoiceCalculator formula = new InvoiceCalculator();

        // lambda
        BigDecimal result = calculate(formula, obj, (f, o) -> f.normal(o));         // 5.97

        // method reference
        BigDecimal result2 = calculate(formula, obj, InvoiceCalculator::normal);    // 5.97

        // lambda
        BigDecimal result3 = calculate(formula, obj, (f, o) -> f.promotion(o));     // 5.37

        // method reference
        BigDecimal result4 = calculate(formula, obj, InvoiceCalculator::promotion); // 5.37

    }

    static BigDecimal calculate(InvoiceCalculator formula, Invoice s1,
                                BiFunction<InvoiceCalculator, Invoice, BigDecimal> func) {
        return func.apply(formula, s1);
    }

}

class InvoiceCalculator {

    public BigDecimal normal(Invoice obj) {
        return obj.getUnitPrice().multiply(BigDecimal.valueOf(obj.qty));
    }

    public BigDecimal promotion(Invoice obj) {
        return obj.getUnitPrice()
                .multiply(BigDecimal.valueOf(obj.qty))
                .multiply(BigDecimal.valueOf(0.9))
                .setScale(2, RoundingMode.HALF_UP);
    }
}

class Invoice {

    String no;
    BigDecimal unitPrice;
    Integer qty;

    // generated by IDE, setters, gettes, constructor, toString
}

第一个参数是invoiceCalculator类型,所以我们可以引用InvoiceCalculator类型的任意对象(f)的实例方法(normal 或者 promotion)

(f, o) -> f.normal(o))
(f, o) -> f.promotion(o))

InvoiceCalculator::normal
InvoiceCalculator::promotion

引用构造函数

Lambda表达式

(args) -> new ClassName(args)

方法引用

ClassName::new

引用默认的构造函数

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class Java8MethodReference4a {

    public static void main(String[] args) {

        // lambda
        Supplier<Map> obj1 = () -> new HashMap();   // default HashMap() constructor
        Map map1 = obj1.get();

        // method reference
        Supplier<Map> obj2 = HashMap::new;
        Map map2 = obj2.get();

        // lambda
        Supplier<Invoice> obj3 = () -> new Invoice(); // default Invoice() constructor
        Invoice invoice1 = obj3.get();

        // method reference
        Supplier<Invoice> obj4 = Invoice::new;
        Invoice invoice2 = obj4.get();

    }

}

class Invoice {

    String no;
    BigDecimal unitPrice;
    Integer qty;

    public Invoice() {
    }

    //... generated by IDE
}

引用一个接收Invoice(Bigdecimal unitPrice)的构造函数

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class Java8MethodReference4b {

    public static void main(String[] args) {

        List<BigDecimal> list = Arrays.asList(
                BigDecimal.valueOf(9.99),
                BigDecimal.valueOf(2.99),
                BigDecimal.valueOf(8.99));

        // lambda
        // List<Invoice> invoices = fakeInvoice(list, (price) -> new Invoice(price));

        // method reference
        List<Invoice> invoices = fakeInvoice(list, Invoice::new);

        invoices.forEach(System.out::println);
    }

    static List<Invoice> fakeInvoice(List<BigDecimal> list, Function<BigDecimal, Invoice> func) {
        List<Invoice> result = new ArrayList<>();

        for (BigDecimal amount : list) {
            result.add(func.apply(amount));
        }
        return result;
    }

}

class Invoice {

    String no;
    BigDecimal unitPrice;
    Integer qty;

    public Invoice(BigDecimal unitPrice) {
        this.unitPrice = unitPrice;
    }

    //... generated by IDE
}

输出

Invoice{no='null', unitPrice=9.99, qty=null}
Invoice{no='null', unitPrice=2.99, qty=null}
Invoice{no='null', unitPrice=8.99, qty=null}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值