体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作 那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢? 答案肯定是没有必要 那我们又是如何使用已经存在的方案呢? 这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
需求: 1、定义一个接口Printable,里面定义一个抽象方法void printString(String s) 2、定义一个测试类,在测试类里面提供一个usePrintable(Printable p),再提供一个main方法,在main方法里面调用usePrintable
体验方法引用的练习
xxxxxxxxxxpackage ch24;
public interface a_5_1Printable {
//带参无返回值的抽象方法 void printString(String s);
}xxxxxxxxxxpackage ch24;
//注意,这接口只是简单体验一下方法引用的用法,后面几节课会由浅入深进行学习public class a_5_2测试 {
public static void main(String[] args) {
//注意:使用Lambda表达式调用接口的方法时,是不需要新建接口的实现类的,前面学Lambda表达式的时候笔记写的很详细了哦,可回去再复习一下
//Lambda表达式 usePrintable((String s)->{ System.out.println(s); }); //因为上面只有一个参数,且方法体只有一条,即可以使用Lambda的省略模式,如下 usePrintable(s->System.out.println(s));//原理:拿参数s,在控制台输出 System.out.println("-------------------------------");
//-----------------------------------------------------------------------------------------------------
//上面Lambda做的事情是有解决方案的,为System.out.println("usePrintable-调用printString方法"),即System.out对象的println方法 //我们可以改进一下上面的Lambda表达式的写法,即使用这节课要学的方法引用,来对Lambda表达式进行改进,如下 //首先,学习一下什么是方法引用符,即双冒号:: usePrintable(System.out::println);//效果跟上面的Lambda表达式是一样的。原理:把参数s,传递给System.out对象的println方法 //println方法优点:可根据传进去的字符串推断出参数就是s
}
private static void usePrintable(a_5_1Printable p){ p.printString("usePrintable-调用printString方法"); }
}
方法引用符
方法引用符:
::该符号为引用运算符,而它所在的表达式被称为方法引用
回顾上面在体验方法引用中的代码 1、Lambda表达式:usePrintable(s->System.out.println(s)); 分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理 2、方法引用:usePrintable(System.out::println); 分析:直接使用System.out中的println方法来取代Lambda,代码更加的简洁
推导与省略 1、如果使用Lambda表达式,那么根据"可推导就是可省略"的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导 2、如果使用方法引用,那么同样可以根据上下文进行推导 3、方法引用是Lambda的孪生兄弟
需求: 1、定义一个接口Printable,里面定义一个抽象方法void printInt(int i) 2、定义一个测试类,在测试类里面提供一个usePrintable(Printable p),再提供一个main方法,在main方法里面调用usePrintable
方法引用符的练习
package ch24;
public interface a_6_1Printable {
//带参无返回值的抽象方法,且形参是int类型 void printInt(int i);
}xxxxxxxxxxpackage ch24;
public class a_6_2测试 {
public static void main(String[] args) { //使用Lambda表达式来调用下面写好的usePrintable方法 usePrintable(i -> System.out.println(i)); //使用方法引用来调用下面写好的usePrintable方法 usePrintable(System.out::println); }
private static void usePrintable(a_6_1Printable p){ p.printInt(100); }
}
引用类方法
Lambda表达式支持的方法引用
常见的引用方式: 1、引用类方法 2、引用对象的实例方法 3、引用类的实例方法 4、引用构造器
我们先学习引用类方法 引用类方法,其实就是引用类的静态方法。格式如下
xxxxxxxxxx类名::静态方法例如Integer类的方法,即public static int parseInt(String s)将此String转换为int类型的数据,怎么写呢,如下
Integer::parseInt
练习: 1、定义一个接口(Converter),里面定义一个抽象方法int convert(String s); 2、定义一个测试类,里面定义一个方法useConverter(Converter c),再定义一个方法main,在main方法里面调用useConvert方法
引用类方法的练习
package ch24;
public interface a_7_1Convert {
int convert(String s);
}xxxxxxxxxxpackage ch24;
public class a_7_2测试 {
public static void main(String[] args) {
//使用Lambda表达式来调用最下面的useConverter方法 useConverter((String s)->{ return Integer.parseInt(s);//输出整型的100 }); //使用Lambda的省略模式优化一下上面的代码,即类型可以省略、当方法只有一个参数时小括号也可以省略、当方法只有一条语句时大括号和分号可以省略 //注意,如果有return的话,要省略return,如下 useConverter( s-> Integer.parseInt(s)//输出整型的100。注意:数不能太大或太小,必须在int的取值范围内才能转换成整型,不然会报错 );
System.out.println("--------------------"); //--------------------------------------------------------------------------------------------------------
//使用引用类方法来调用最下面的useConverter方法,也是我们这节课讲的主要知识 useConverter(Integer::parseInt);
//---------------------------------------------------------------------------------------------------------
//总结上面的两大部分的代码:Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数 //简单理解就是,上面的Lambda表达式的参数(即s)只有一个的时候,我们使用引用类方法时parseInt就对应的是一个参数。如果Lambda表达式的 //参数不是一个而是多个的时候,那么引用类方法时parseInt就对应的是多个参数。就理解为引用类方法非常好用,不需要我们去考虑参数
}
private static void useConverter(a_7_1Convert c){ int number = c.convert("100");//要接收一下,因为我们在接口里写的convert方法是把字符串型转换成整型,最后再返回出来,有返回值 System.out.println(number); }
}
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法。格式如下
对象::成员方法注意,String类中有一个方法:public String toUpperCase() 作用是将此String所有字符转换为大写
练习: 1、定义一个类(PrintString),里面定义一个方法即public void printUpper(String s) 作用是把字符串参数变成大写的数据 2、定义一个接口(Printer),里面定义一个抽象方法即void printUpperCase(String s) 3、定义一个测试类,里面定义一个方法usePrinter(Printer p),再定义一个main方法,并在main方法里面调用usePrinter方法
引用对象实例方法的练习
package ch24;
public interface a_8_1Printer {
//带参无返回值的抽象方法 void printUpperCase(String s);
}xxxxxxxxxxpackage ch24;
public class a_8_2PrintString {
public void printUpper(String s){ String result = s.toUpperCase();//把字符串变成大写的字符串 System.out.println(result); }
}xxxxxxxxxxpackage ch24;
public class a_8_3测试 {
public static void main(String[] args) {
//使用Lambda表达式来调用最下面的usePrinter方法 usePrinter((String s)->{ //String result = s.toUpperCase();//把字符串变成大写的字符串 //System.out.println(result); //把上面两行合并为一行,如下 System.out.println(s.toUpperCase()); }); //使用Lambda的省略模式优化一下上面的代码,即类型可以省略、当方法只有一个参数时小括号也可以省略、当方法只有一条语句时大括号和分号可以省略 usePrinter(s->System.out.println(s.toUpperCase()));
//注意,上面那些代码都没有用到我们的'a_8_2PrintString'类,会在下面讲到,很重要好好学 System.out.println("--------------------"); //--------------------------------------------------------------------------------------------------------
//使用引用对象的实例方法来调用最下面的usePrinter方法,也是我们这节课讲的主要知识,如何引用对象的实例方法 //我们新建一个'a_8_2PrintString'类,在里面写上printUpper方法。上面的那些代码是在方法体里面写'把字符串变成大写字符串'的方法, //而我们不需要在方法体里面写,我们在'a_8_2PrintString'类里面的printUpper方法里面写,写好之后就可以进行下面的代码操作了 //引用对象的实例方法,需要先创建一个对象 a_8_2PrintString ps = new a_8_2PrintString(); usePrinter(ps::printUpper);//用'a_8_2PrintString'类的对象(即ps)调用'a_8_2PrintString'类里面的'printUpper'方法 //这里的输出跟最上面的Lambda表达式的效果是一样的
//总结上面的三大部分的代码:Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数 //简单理解就是,上面的Lambda表达式的参数(即s)只有一个的时候,我们使用对象的实例化方法时printUpper就对应的是一个参数。如果Lambda //表达式的参数不是一个而是多个的时候,那么对象的实例化方法printUpper就对应的是多个参数
}
private static void usePrinter(a_8_1Printer p){ p.printUpperCase("Hello World"); }
}
引用类的实例方法
引用对象的实例方法,其实就引用类中的成员方法。格式如下
类名::成员方法注意,String类中有一个方法:public String subString(int beginIndex,int endIndex),作用是从beginIndex开始到endIndex结束,截取字符串。返回一个字串,字串的长度为endIndex~beginIndex。简单理解上面那个方法的作用就是截取字符串
练习: 1、定义一个接口(MyString),里面定义一个抽象方法即String mySubString(String s,int x,int y); 3、定义一个测试类,里面定义一个方法useMyString(MyString my),再定义一个main方法,并在main方法里面调用useMyString方法
引用类实例方法的练习
xxxxxxxxxxpackage ch24;
public interface a_9_1MyString {
//带参带返回值的抽象方法,参数是三个形参 String mySubString(String s,int x,int y);
}xxxxxxxxxxpackage ch24;
public class a_9_2测试 {
public static void main(String[] args) {
//使用Lambda表达式来调用最下面的useMyString方法 useMyString((String s,int x,int y)->{ return s.substring(x,y); //substring是截取字符串的方法,结合最下面的useMyString方法,可知是把字符串的第2个索引到第9个索引之间的全部字符截取下来 //注意:包含2索引,不包含9索引。空格也占一个索引 //这里的截取字符串的操作是通过调用String类中的substring方法 }); //使用Lambda的省略模式优化一下上面的代码,即参数类型可以省略、当方法只有一条语句时大括号和分号可以省略 useMyString((s,x,y)->s.substring(x,y));
System.out.println("--------------------"); //--------------------------------------------------------------------------------------------------------
//使用引用类的实例方法来调用最下面的useMyString方法,也是我们这节课讲的主要知识,如何引用类的实例方法 useMyString(String::substring);//使用String类中的substring方法。注意:String类不是我们新建的,是系统有的
//总结上面的两大部分的代码:Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的全部参数全部传递给该方法,作为该方法的参数 //简单理解就是在15行的useMyString方法有3个参数,第一个参数s调用了22行的substring,在15行后面剩下的参数如x和y就直接作为 //22行的substring方法的参数。应该很好理解
}
private static void useMyString(a_9_1MyString my){ String s = my.mySubString("hello world", 2, 9);//右边的方法会返回一个String类型的结果 System.out.println(s); } }
引用构造器
引用构造器,其实就是引用构造方法。格式如下
类名::new练习: 1、定义一个类(Student),里面有两个成员变量(name,age)。并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法 2、定义一个接口(StudentBuilder),里面定义一个抽象方法即Student build(String name,int age) 3、定义一个测试类,里面定义一个方法useStudentBuilder(StudentBuilder s),再定义一个main方法,并在main方法里面调用useStudentBuilder方法
引用构造器的练习
xxxxxxxxxxpackage ch24;
public interface a_10_1StudentBuilder {
//带参带返回值的抽象方法 a_10_2Student build(String name,int age);
}xxxxxxxxxxpackage ch24;
//快捷键:Alt+insertpublic class a_10_2Student {
//成员变量 private String name; private int age;
//无参构造方法 public a_10_2Student() { }
//带参构造方法 public a_10_2Student(String name,int age) { this.name = name; this.age = age; }
//成员变量对应的get和set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }
}xxxxxxxxxxpackage ch24;
public class a_10_3测试 {
public static void main(String[] args) {
//使用Lambda表达式来调用最下面的useStudentBuilder方法\ useStudentBuilder((String name,int age)->{ //由于useStudentBuilder方法返回的是a_10_2Student类型的,所以我们先创建一个a_10_2Student类型的对象,如下 //a_10_2Student s = new a_10_2Student(name,age); //return s; //上面那两行可以合并为一行,如下 return new a_10_2Student(name,age); });
//使用Lambda的省略模式优化一下上面的代码,即参数类型可以省略、当方法只有一条语句时大括号和分号可以省略 useStudentBuilder((name,age)->new a_10_2Student(name,age));
System.out.println("--------------------"); //--------------------------------------------------------------------------------------------------------
//使用引用构造器来调用最下面的useStudentBuilder方法,也是我们这节课讲的主要知识,如何引用构造器 useStudentBuilder(a_10_2Student::new);//即表示调用了a_10_2Student类的构造方法,跟最上面的Lambda表达式的效果是一样的
//总结上面的两大部分的代码:Lambda表达式被类的实例方法替代的时候,它的形式参数全部传递给构造器作为参数 //简单理解就是上面17行的name和age参数传递给了24行的a_10_2Student类的带两个构造参数的构造方法 //上面那行注释的'带两个构造参数的构造方法'指的就是a_10_2Student类里面第15行的带参构造方法,那个构造方法就是带两个构造参数的
}
private static void useStudentBuilder(a_10_1StudentBuilder sbd) { a_10_2Student s = sbd.build("张三", 18);//右边是有返回值的,因为我们在接口中定义的抽象方法是带返回值的 System.out.println(s.getName() + "," + s.getAge());
}
}