如何深入掌握Java 8中双冒号::的用法?

2026-05-21 06:493阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2115个文字,预计阅读时间需要9分钟。

如何深入掌握Java 8中双冒号::的用法?

一、方法引用的使用

在Java 8中,我们可以使用Lambda表达式创建匿名方法。但有时,我们也可以通过方法引用来简化这一过程。

方法引用允许我们直接引用现有的方法而不是编写一个匿名方法。以下是如何使用方法引用创建匿名方法的例子:

java// 使用Lambda表达式创建匿名方法Runnable r1=() -> System.out.println(Hello, World!);

// 使用方法引用替代Lambda表达式Runnable r2=System.out::println;

方法引用在某些情况下比Lambda表达式更简洁,特别是在只需要调用现有方法的情况下。但需要注意的是,方法引用除了调用方法外,不执行任何其他操作。

二、方法引用与Lambda表达式的区别

尽管方法引用和Lambda表达式在某些情况下可以互换使用,但它们之间仍有一些区别:

1. 可读性:方法引用通常更简洁,尤其是在调用单个方法时,这使得代码更易读。

2. 性能:方法引用可能在某些情况下具有更好的性能,因为它们避免了创建额外的匿名方法对象。

3. 灵活性:Lambda表达式提供了更多的灵活性,例如在需要捕获外部变量、实现接口或使用默认方法时。

三、使用方法引用调用现有方法

在某些情况下,我们可能需要通过方法引用来调用现有方法,以下是一个示例:

java// 假设有一个接口和一个实现类interface Calculator { int calculate(int a, int b);}

class SumCalculator implements Calculator { @Override public int calculate(int a, int b) { return a + b; }}

// 使用方法引用调用现有方法Calculator sum=SumCalculator::calculate;int result=sum.calculate(10, 20);System.out.println(Result: + result);

在这个例子中,我们通过方法引用调用了`SumCalculator`类的`calculate`方法。

总结:

方法引用在Java 8中是一个非常有用的特性,它可以帮助我们更简洁地编写代码,特别是在调用现有方法时。虽然方法引用和Lambda表达式在某些情况下可以互换使用,但它们之间仍有一些区别。在编写代码时,根据具体情况选择使用方法引用还是Lambda表达式会更加合适。

一、方法引用

java8允许我们使用lambda表达式创建匿名方法。但有时lambda表达式除了调用现有方法之外什么也不做。在这些情况下,通过名称引用现有的方法,通常能更直白的表现出方法的调用过程。对于已经存在的且具有方法名称的方法,它其实是简洁且易于读取的一种lambda表达式,或者说是对lambda表达式的一种进一步简化。

现在我们来看看下面这个“person”类:

public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public Calendar getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); }}

假设你的社交网络应用程序的成员包含在一个数组中,并且你希望按年龄对数组进行排序。你可以使用以下代码:

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); class PersonAgeComparator implements Comparator<Person> { public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } Arrays.sort(rosterAsArray, new PersonAgeComparator());

上面最后一行代码:中的sort方法源码如下:

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

注意,接口comparator是一个功能接口。因此,你可以使用lambda表达式,而不是定义并创建实现comparator的类的新实例,所以上上面的代码可以用lambda表达式改写成下面这样:

Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); });

但是,我们已经在Person bean对象中提前写好了用来比较两个person对象的出生日期的方法:,所以你其实可以在lambda表达式的主体中直接调用这个方法方法:

Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); } );

因为这个上面这个lambda表达式是在调用现有的方法,所以我们这里就可以使用上面提到的使用方法引用方式(及双冒号 ::),而不是之前我们熟悉的lambda表达式:

Arrays.sort(rosterAsArray, Person::compareByAge);

方法引用 person::comparebyage 在语义上与lambda表达式(a,b)->person.comparebyage(a,b)相同。他们都有以下特点:

  • 它的形参列表复制自comparator<person>.compare,即(Person, Person)
  • 它的主体调用方法是:person.comparebyage。

二、方法引用的种类(哪些场景可以使用方法引用)

有四种方法引用:

种类

案例 引用静态方法 ContainingClass::staticMethodName 对特定对象的实例方法的引用 containingObject::instanceMethodName 对特定类型的任意对象的实例方法的引用 ContainingType::methodName 对构造函数的引用 ClassName::new

1、引用静态方法

例如:

Person::comparebyage 是对Person类的静态方法 comparebyage 的引用。

2、引用特定对象的实例方法

以下是对特定对象的实例方法的引用示例:

class ComparisonProvider { public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName()); } public int compareByAge(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } ComparisonProvider myComparisonProvider = new ComparisonProvider(); Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

方法引用 myComparisonProvider::compareByName调用对象myComparisonProvider的一部分方法compareByName。然后JRE会自动推断方法类型参数,在这种情况下为(Person, Person)

3、引用特定类型的任意对象的实例方法

以下是对特定类型的任意对象的实例方法的引用示例:

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

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

这里官网原文可能说的不是很清楚,也有点拗口,多说一句:

如何理解:对特定类型的任意对象的实例方法的引用 这句话呢?

实际上上面代码没有在特定实例(也就是没有new一个指定的String 实例出来,像这样:String s = new String() ,s就是一个指定的String对象的实例)上引用方法 compareTogignoreCase ,而是在String类自身引用的。

我们进入Arrays.sort(xx,xx)方法源码,我们可以看到这个数组排序方法底层算法涉及循环,那么这行代码我们可以想象它会在每循环到数组的一个String元素时,以这个String元素所属类型对象(其实就是String对象)的身份去调用compareTogignoreCase方法,即:

会调用的compareTogignoreCase --》String Barbara = new String();Barbara.compareTogignoreCase

会调用的compareTogignoreCase--》String James= new String();James.compareTogignoreCase

如何深入掌握Java 8中双冒号::的用法?

以此类推。。。

我可能解释的也不太好,主要就是想语言上理解一下:对特定类型的 任意对象的 实例方法 的引用

4、引用构造函数

你可以通过使用name new 的方式引用构造函数,这与静态方法的引用方式类似。

下面这个示例方法,是将元素从一个集合复制到另一个集合。我们将以这个方法为例,讲解如何使用方法引用的方式引用构造函数。

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>> DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) { DEST result = collectionFactory.get(); for (T t : sourceCollection) { result.add(t); } return result; }

函数接口supplier包含一个无参但返回一个对象的get方法,源码可见,他返回的类型就是泛型的类型,如下:

因此,可以使用lambda表达式调用方法transferElements,如下所示:

Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });

可以使用构造函数引用代替lambda表达式,如下所示:

Set<Person> rosterSet = transferElements(roster, HashSet::new);

上面代码,Java编译器执行的时候会自动推断出这里想要创建一个包含Person类型元素的hashset集合。当然你也可以显示的指定类型,如下:

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);

到此这篇关于深入理解Java8双冒号::的使用的文章就介绍到这了,更多相关Java8双冒号::内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

本文共计2115个文字,预计阅读时间需要9分钟。

如何深入掌握Java 8中双冒号::的用法?

一、方法引用的使用

在Java 8中,我们可以使用Lambda表达式创建匿名方法。但有时,我们也可以通过方法引用来简化这一过程。

方法引用允许我们直接引用现有的方法而不是编写一个匿名方法。以下是如何使用方法引用创建匿名方法的例子:

java// 使用Lambda表达式创建匿名方法Runnable r1=() -> System.out.println(Hello, World!);

// 使用方法引用替代Lambda表达式Runnable r2=System.out::println;

方法引用在某些情况下比Lambda表达式更简洁,特别是在只需要调用现有方法的情况下。但需要注意的是,方法引用除了调用方法外,不执行任何其他操作。

二、方法引用与Lambda表达式的区别

尽管方法引用和Lambda表达式在某些情况下可以互换使用,但它们之间仍有一些区别:

1. 可读性:方法引用通常更简洁,尤其是在调用单个方法时,这使得代码更易读。

2. 性能:方法引用可能在某些情况下具有更好的性能,因为它们避免了创建额外的匿名方法对象。

3. 灵活性:Lambda表达式提供了更多的灵活性,例如在需要捕获外部变量、实现接口或使用默认方法时。

三、使用方法引用调用现有方法

在某些情况下,我们可能需要通过方法引用来调用现有方法,以下是一个示例:

java// 假设有一个接口和一个实现类interface Calculator { int calculate(int a, int b);}

class SumCalculator implements Calculator { @Override public int calculate(int a, int b) { return a + b; }}

// 使用方法引用调用现有方法Calculator sum=SumCalculator::calculate;int result=sum.calculate(10, 20);System.out.println(Result: + result);

在这个例子中,我们通过方法引用调用了`SumCalculator`类的`calculate`方法。

总结:

方法引用在Java 8中是一个非常有用的特性,它可以帮助我们更简洁地编写代码,特别是在调用现有方法时。虽然方法引用和Lambda表达式在某些情况下可以互换使用,但它们之间仍有一些区别。在编写代码时,根据具体情况选择使用方法引用还是Lambda表达式会更加合适。

一、方法引用

java8允许我们使用lambda表达式创建匿名方法。但有时lambda表达式除了调用现有方法之外什么也不做。在这些情况下,通过名称引用现有的方法,通常能更直白的表现出方法的调用过程。对于已经存在的且具有方法名称的方法,它其实是简洁且易于读取的一种lambda表达式,或者说是对lambda表达式的一种进一步简化。

现在我们来看看下面这个“person”类:

public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public Calendar getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); }}

假设你的社交网络应用程序的成员包含在一个数组中,并且你希望按年龄对数组进行排序。你可以使用以下代码:

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); class PersonAgeComparator implements Comparator<Person> { public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } Arrays.sort(rosterAsArray, new PersonAgeComparator());

上面最后一行代码:中的sort方法源码如下:

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

注意,接口comparator是一个功能接口。因此,你可以使用lambda表达式,而不是定义并创建实现comparator的类的新实例,所以上上面的代码可以用lambda表达式改写成下面这样:

Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); });

但是,我们已经在Person bean对象中提前写好了用来比较两个person对象的出生日期的方法:,所以你其实可以在lambda表达式的主体中直接调用这个方法方法:

Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); } );

因为这个上面这个lambda表达式是在调用现有的方法,所以我们这里就可以使用上面提到的使用方法引用方式(及双冒号 ::),而不是之前我们熟悉的lambda表达式:

Arrays.sort(rosterAsArray, Person::compareByAge);

方法引用 person::comparebyage 在语义上与lambda表达式(a,b)->person.comparebyage(a,b)相同。他们都有以下特点:

  • 它的形参列表复制自comparator<person>.compare,即(Person, Person)
  • 它的主体调用方法是:person.comparebyage。

二、方法引用的种类(哪些场景可以使用方法引用)

有四种方法引用:

种类

案例 引用静态方法 ContainingClass::staticMethodName 对特定对象的实例方法的引用 containingObject::instanceMethodName 对特定类型的任意对象的实例方法的引用 ContainingType::methodName 对构造函数的引用 ClassName::new

1、引用静态方法

例如:

Person::comparebyage 是对Person类的静态方法 comparebyage 的引用。

2、引用特定对象的实例方法

以下是对特定对象的实例方法的引用示例:

class ComparisonProvider { public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName()); } public int compareByAge(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } ComparisonProvider myComparisonProvider = new ComparisonProvider(); Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

方法引用 myComparisonProvider::compareByName调用对象myComparisonProvider的一部分方法compareByName。然后JRE会自动推断方法类型参数,在这种情况下为(Person, Person)

3、引用特定类型的任意对象的实例方法

以下是对特定类型的任意对象的实例方法的引用示例:

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

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

这里官网原文可能说的不是很清楚,也有点拗口,多说一句:

如何理解:对特定类型的任意对象的实例方法的引用 这句话呢?

实际上上面代码没有在特定实例(也就是没有new一个指定的String 实例出来,像这样:String s = new String() ,s就是一个指定的String对象的实例)上引用方法 compareTogignoreCase ,而是在String类自身引用的。

我们进入Arrays.sort(xx,xx)方法源码,我们可以看到这个数组排序方法底层算法涉及循环,那么这行代码我们可以想象它会在每循环到数组的一个String元素时,以这个String元素所属类型对象(其实就是String对象)的身份去调用compareTogignoreCase方法,即:

会调用的compareTogignoreCase --》String Barbara = new String();Barbara.compareTogignoreCase

会调用的compareTogignoreCase--》String James= new String();James.compareTogignoreCase

如何深入掌握Java 8中双冒号::的用法?

以此类推。。。

我可能解释的也不太好,主要就是想语言上理解一下:对特定类型的 任意对象的 实例方法 的引用

4、引用构造函数

你可以通过使用name new 的方式引用构造函数,这与静态方法的引用方式类似。

下面这个示例方法,是将元素从一个集合复制到另一个集合。我们将以这个方法为例,讲解如何使用方法引用的方式引用构造函数。

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>> DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) { DEST result = collectionFactory.get(); for (T t : sourceCollection) { result.add(t); } return result; }

函数接口supplier包含一个无参但返回一个对象的get方法,源码可见,他返回的类型就是泛型的类型,如下:

因此,可以使用lambda表达式调用方法transferElements,如下所示:

Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });

可以使用构造函数引用代替lambda表达式,如下所示:

Set<Person> rosterSet = transferElements(roster, HashSet::new);

上面代码,Java编译器执行的时候会自动推断出这里想要创建一个包含Person类型元素的hashset集合。当然你也可以显示的指定类型,如下:

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);

到此这篇关于深入理解Java8双冒号::的使用的文章就介绍到这了,更多相关Java8双冒号::内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!