1.初入方法引用
2.通过对象名引用成员方法
3.通过类名引用静态方法
4.通过super引用成员方法
5.通过this引用成员方法
6.类的构造器引用
7.数组的构造方法的引用
1.初入方法引用
1.冗余的Lambda表达式解决方案:
例子:
package untl1;
public class MyPrint {
public static void print(printable pri)
{
pri.print("helloworld");
}
public static void main(String[] args) {
print((str)->{
System.out.println(str);
});
}
}
@FunctionalInterface
interface printable{
void print(String str);
}
运行结果:
helloworld
如果不懂Lambda可以参照这一篇博客:Lambda表达式详解
分析上边的代码;
其中 prin 方法只管调用 printable 接口的 print 方法,而并不管 print 方法的具体实现逻辑会将字符串 打印到什么地方去。而 main 方法通过Lambda表达式指定了函数式接口 Printable 的具体操作方案为:拿到 String(类型可推导,所以可省略)数据后,在控制台中输出它。这段代码的问题在于,对字符串进行控制台打印输出的操作方案,明明已经有了现成的实现,那就是 System.out 对象中的 println(String) 方法。既然Lambda希望做的事情就是调用 println(String) 方法,那何必自己手动调 用呢
啥意思呢看用方法引用的例子:
package untl1;
public class MyPrint {
public static void print(printable pri)
{
pri.print("helloworld");
}
public static void main(String[] args) {
print(System.out::println);
}
}
@FunctionalInterface
interface printable{
void print(String str);
}
运行结果:
helloworld
卧槽,这么高级
下面是对上边代码的解释:
1.请注意其中的双冒号
::
写法,这被称为“方法引用”,而双冒号是一种新的语法,双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
2.例如上例中,System.out 对象中有一个重载的 println(String) 方法恰好就是我们所需要的
。那么对于 printString 方法的函数式接口参数,对于上边的两种写法是等效的
对于例子中两种写法的对比:
写法类型 | 格式 | 语义 |
---|---|---|
Lambda表达式 | s -> System.out.println(s); | 拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。 |
方法引用 | System.out::println | 直接让 System.out 中的 println 方法来取代Lambda。两种写法的执行效果完全一 样,而第二种方法引用的写法复用了已有方案,更加简洁。 注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常 |
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都 将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。 函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟
上述的第二个例子就是把已经存在的System.out这个对象通过已经存在的printfln这个方法
再细品这句话:如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
2.通过对象名引用成员方法
其实和上边的例子差不多再来一个例子:
package untl1;
public class MyPrint {
public void printString(String str)
{
System.out.println(str.toUpperCase());
}
public static void prin(printable pri)
{
pri.print("helloworld");
}
public static void main(String[] args) {
MyPrint p=new MyPrint();
prin(p::printString);
}
}
@FunctionalInterface
interface printable{
void print(String str);
}
运行结果:
HELLOWORLD
你品你细品:如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
3.通过类名引用静态方法
我们分别用Lambda表达式和方法引用来举例对比,求一个数的绝对值,然和再输出
用Lambda表达式:
package untl1;
public class MyPrint {
public static void aaa(int num,printabs pri)
{
System.out.println(pri.aboutabs(num));
}
public static void main(String[] args) {
aaa(-100,num->Math.abs(num));
}
}
@FunctionalInterface
interface printabs{
int aboutabs(int a);
}
输出结果:
100
用方法的引用:
package untl1;
public class MyPrint {
public static void aaa(int num,printabs pri)
{
System.out.println(pri.aboutabs(num));
}
public static void main(String[] args) {
aaa(-100,Math::abs);
}
}
@FunctionalInterface
interface printabs{
int aboutabs(int a);
}
输出结果:
100
4.通过super引用成员方法
如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。
废话不多说,先用Lambda举例子:
package untl1;
public class MyPrint extends human{
public void sayhello()
{
System.out.println("hello我是human的儿子");
}
public void method(Greetable g)
{
g.greet();
}
public void show()
{
method(()->{
human h=new human();
h.sayhello();
});
}
public static void main(String[] args) {
MyPrint p=new MyPrint();
p.show();
}
}
class human{
public void sayhello()
{
System.out.println("hello,我是human");
}
}
@FunctionalInterface
interface Greetable{
void greet();
}
运行结果:
hello,我是human
那么上边的代码用方法的引用:
package untl1;
public class MyPrint extends human{
public void sayhello()
{
System.out.println("hello我是human的儿子");
}
public void method(Greetable g)
{
g.greet();
}
public void show()
{
method(super::sayhello);
}
public static void main(String[] args) {
MyPrint p=new MyPrint();
p.show();
}
}
class human{
public void sayhello()
{
System.out.println("hello,我是human");
}
}
@FunctionalInterface
interface Greetable{
void greet();
}
运行结果:
hello,我是human
其实和对象引用方法名一样的
5.通过this引用成员方法
this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法
package untl1;
public class MyPrint extends human{
public void sayhello()
{
System.out.println("hello我是human的儿子");
}
public void method(Greetable g)
{
g.greet();
}
public void show()
{
method(this::sayhello);
}
public static void main(String[] args) {
MyPrint p=new MyPrint();
p.show();
}
}
class human{
public void sayhello()
{
System.out.println("hello,我是human");
}
}
@FunctionalInterface
interface Greetable{
void greet();
}
输出结果:
hello我是human的儿子
6.类的构造器的引用
由于构造器的名称与类名完全一样,并不固定。所以构造器引用使用 类名称::new 的格式表示
例子(Lambda表达式):
package untl1;
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void printname(String name,PersonBuilder per)
{
System.out.println(per.buildPerson(name).getName());
}
public static void main(String[] args) {
printname("哈哈",name->new Person(name));
}
}
interface PersonBuilder {
Person buildPerson(String name);
}
运行结果:
哈哈
用类构造器的引用:
package untl1;
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void printname(String name,PersonBuilder per)
{
System.out.println(per.buildPerson(name).getName());
}
public static void main(String[] args) {
printname("哈哈",Person::new);
}
}
interface PersonBuilder {
Person buildPerson(String name);
}
运行结果:
哈哈
7.数组的构造器引用
数组也是 Object 的子类对象,所以同样具有构造器,只是语法稍有不同
例子:
package untl1;
public class Person {
public static int[] createArray(int length,ArrayBuild ab)
{
return ab.builderArray(length);
}
public static void main(String[] args) {
// int arr[]=createArray(10,len->new int[len]);
int arr[]=createArray(10,int[]::new);
System.out.println(arr.length);
}
}
interface ArrayBuild{
int [] builderArray(int length);
}
运行结果:
10