体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作 那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢? 答案肯定是没有必要 那我们又是如何使用已经存在的方案呢? 这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
需求: 1、定义一个接口Printable,里面定义一个抽象方法void printString(String s) 2、定义一个测试类,在测试类里面提供一个usePrintable(Printable p),再提供一个main方法,在main方法里面调用usePrintable
体验方法引用的练习
xxxxxxxxxx
package ch24;
public interface a_5_1Printable {
//带参无返回值的抽象方法
void printString(String s);
}
xxxxxxxxxx
package 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);
}
xxxxxxxxxx
package 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);
}
xxxxxxxxxx
package 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);
}
xxxxxxxxxx
package ch24;
public class a_8_2PrintString {
public void printUpper(String s){
String result = s.toUpperCase();//把字符串变成大写的字符串
System.out.println(result);
}
}
xxxxxxxxxx
package 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方法
引用类实例方法的练习
xxxxxxxxxx
package ch24;
public interface a_9_1MyString {
//带参带返回值的抽象方法,参数是三个形参
String mySubString(String s,int x,int y);
}
xxxxxxxxxx
package 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方法
引用构造器的练习
xxxxxxxxxx
package ch24;
public interface a_10_1StudentBuilder {
//带参带返回值的抽象方法
a_10_2Student build(String name,int age);
}
xxxxxxxxxx
package ch24;
//快捷键:Alt+insert
public 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;
}
}
xxxxxxxxxx
package 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());
}
}