接口组成更新与方法引用
1.接口组成更新概述
1.1 接口组成
- 常量:public static final
- 抽象方法:public abstract
- 默认方法(Java8)
- 静态方法(Java8)
- 私有方法(Java9)
2.默认方法
2.1 格式
接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表){}
- 范例:public default void show(){}
public default void show3(){
System.out.println("show");
}
2.2 注意事项
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不被强制重写。但是可以被重写,重写时去掉default关键字
- public可以省略,default不能省略
需求:
- 定义一个接口MyInterface,里面有两个抽象方法
- void show1();
- void show2();
- 定义两个实现类:
- MyInterfaceImplOne
- MyInterfaceImplTwo
- 定义测试类:
- MyInterfaceDemo
- 在主方法中,按照多态方式创建对象并使用
MyInterface接口:
public interface MyInterface {
void show1();
void show2();
// void show3();
// public default void show3(){
// System.out.println("show");
// }
default void show3(){
System.out.println("show");
}
}
MyInterfaceImplOne实现类:
public class MyInterfaceImplOne implements MyInterface {
@Override
public void show1() {
System.out.println("one show1");
}
@Override
public void show2() {
System.out.println("one show2");
}
@Override
public void show3() {
System.out.println("one show3");
}
}
MyInterfaceImplTwo实现类:
public class MyInterfaceImplTwo implements MyInterface {
@Override
public void show1() {
System.out.println("two show1");
}
@Override
public void show2() {
System.out.println("two show2");
}
}
MyInterfaceDemo测试类:
public class MyInterfaceDemo {
public static void main(String[] args) {
//多态方式创建对象
MyInterface my = new MyInterfaceImplOne();
my.show1();
my.show2();
my.show3();
}
}
3.静态方法
3.1 格式
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例:public static void show(){}
static void show(){}
2.2 注意事项
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或对象名调用
- public可以省略,static不能省略
需求:
- 定义一个接口Inter,里面有三个方法,一个抽象方法,一个默认方法,一个静态方法
- void show()
- default void method() {}
- public static void test(){}
- 定义接口的一个实现类:
- InterImpl
- 定义测试类:
- InterDemo
- 在主方法中,按照多态方式创建对象并使用
Inter接口:
public interface Inter {
void show();
default void method(){
System.out.println("Inter中的默认方法method()执行了");
}
public static void test(){
System.out.println("Inter中的静态方法test()执行了");
}
}
Flyable接口:
public interface Flyable {
static void test(){
System.out.println("Flyable中的静态方法test()执行了");
}
}
InterImpl实现类:
public class InterImpl implements Inter,Flyable {
@Override
public void show(){
System.out.println("show()方法执行了");
}
}
InterDemo测试类:
public class InterDemo {
public static void main(String[] args) {
//按照多态方式创建对象并使用
Inter i = new InterImpl();
i.show();//show()方法执行了
i.method();//Inter中的默认方法method()执行了
//i.test();//Static method may be invoked on containing interface class only
Inter.test();//Inter中的静态方法test()执行了
//InterImpl.test();//Static method may be invoked on containing interface class only
Flyable.test();//Flyable中的静态方法test()执行了
}
}
4.私有方法
4.1 必然性
Java9新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔:Java8允许在接口中定义带方法体的默认方法和静态方法。
这样就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性
4.1 格式
接口中私有方法的定义格式:
- 格式1: private 返回值类型 方法名(参数列表){}
- 范例1: private void show(){}
- 格式2: private static 返回值类型 方法名(参数列表){}
- 范例2: private static void method(){}
4.2 注意事项
接口中私有方法的注意事项:
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
需求:
- 定义一个接口Inter,里面有四个方法,两个默认方法,两个静态方法
- default void show1(){}
- default void show2(){}
- static void method1(){}
- static void method2(){}
- 定义接口的一个实现类:
- InterImpl
- 定义测试类:
- InterDemo
- 在主方法中,按照多态方式创建对象并使用
Inter接口:
package com.advanced.myinterface.demo03private;
public interface Inter {
default void show1(){
System.out.println("show1开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
// show();
method();
System.out.println("show1结束执行");
}
default void show2(){
System.out.println("show2开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
// show();
method();
System.out.println("show2结束执行");
}
private void show(){
System.out.println("初级工程师");
System.out.println("中级工程师");
System.out.println("高级工程师");
}
static void method1(){
System.out.println("method1开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
method();
System.out.println("method1结束执行");
// show();
}
static void method2(){
System.out.println("method2开始执行");
// System.out.println("初级工程师");
// System.out.println("中级工程师");
// System.out.println("高级工程师");
method();
System.out.println("method2结束执行");
}
private static void method(){
System.out.println("初级工程师");
System.out.println("中级工程师");
System.out.println("高级工程师");
}
}
InterImpl实现类:
public class InterImpl implements Inter {}
InterDemo测试类:
public class InterDemo {
public static void main(String[] args) {
Inter i = new InterImpl();
i.show1();
System.out.println("==========(@ _ @)==========");
i.show2();
System.out.println("==========(@ _ @)==========");
Inter.method1();
System.out.println("==========(@ _ @)==========");
Inter.method2();
}
}
5.方法引用
5.1 体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的参数代码就是一种解决方案:拿参数做操作
那么考虑一种情况,如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?
答案肯定是没有必要的
那么我们又是如何使用已经存在的方案的呢?
这就是我们要将的方法引用,我们是通过方法的引用来使用已经存在的方案
需求:
- 定义一个接口(Printable):里面定义一个抽象方法:void printString(String s);
- 定义一个测试类(PrintableDemo),在测试类中有两个方法
- 一个方法是:usePrintable(Printable p)
- 一个方法是主方法,在主方法中调用usePrintable()方法
Printable接口:
public interface Printable {
void printString(String s);
}
PrintableDemo测试类:
public class PrintableDemo {
public static void main(String[] args) {
//匿名内部类
// usePrintable(new Printable() {
// @Override
// public void printString(String s) {
// System.out.println(s);
// }
// });
//Lambda
usePrintable(s -> System.out.println(s));
System.out.println("hello,world");
//方法引用符: ::
usePrintable(System.out::println);
//可推导的就是可省略的
}
private static void usePrintable(Printable p){
p.printString("hello,world");
}
}
5.2 方法引用符
方法引用符
- ::该符号为引用运算符,而它所在的表达式被称为方法引用
回顾以下我们在‘体验方法引用’中的代码
-
Lambda表达式:usePrintable(s -> System.out.println(s));
分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
-
方法引用:usePrintable(System.out::println);
分析:直接使用System.out中的println方法来取代Lambda,代码更加简洁
推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
需求:
- 定义一个接口(Printable):里面定义一个抽象方法:void printString(int i);
- 定义一个测试类(PrintableDemo),在测试类中有两个方法
- 一个方法是:usePrintable(Printable p)
- 一个方法是主方法,在主方法中调用usePrintable()方法
Printable接口:
public interface Printable {
void printString(int i);
}
PrintableDemo测试类:
public class PrintableDemo {
public static void main(String[] args) {
//在主方法中调用usePrintable()方法
usePrintable(i -> System.out.println(i));
//方法引用
usePrintable(System.out::println);
}
private static void usePrintable(Printable p) {
p.printString(666);
}
}
5.3 Lambda表达式支持的方法引用
常见的引用方式:
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
5.4 引用类方法
引用类方法,其实就是引用类的静态方法
- 格式:类名::静态方法
- 范例:Integer::parseInt
- Integer类的方法:public static int parseInt(String s)将此String转换为int类型数据
练习:
- 定义一个接口(Converter),里面定义一个抽象方法:int convert(String s);
- 定义一个测试类(ConverterDemo),在测试类中提供两个方法
- 一个方法是:useConverter(Converter c)
- 一个方法是主方法,在主方法中调用useConverter()方法
Converter接口:
public interface Converter {
int convert(String s);
}
ConverterDemo测试类:
public class ConverterDemo {
public static void main(String[] args) {
//在主方法中调用useConverter()方法
useConverter(s -> Integer.parseInt(s));
//方法引用
useConverter(Integer::parseInt);
//Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
private static void useConverter(Converter c) {
int num = c.convert("666");
System.out.println(num);
}
}
5.5 引用对象的实例方法
引用对象的实例方法,其实就是引用类中的成员方法
- 格式:对象::成员方法
- 范例:“HelloWorld”::toUpperCase
- String类的方法:public String toUpperCase()将此String所有字符转换为大写
练习:
- 定义一个类(PrintString),里面定义一个方法
- public void printUpper(String s):把字符串参数变为大写形式,然后在控制台输出
- 定义一个接口(Printer),里面定义一个抽象方法
- void printUpperCase(String s)
- 定义一个测试类(PrinterDemo),在测试类中提供两个方法
- 一个方法是:usePrinter(Printer p)
- 一个方法是主方法,在主方法中调用usePrinter方法
PrintString类:
public class PrintString {
//把字符串参数变为大写形式,然后在控制台输出
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
Printer接口:
public interface Printer {
void printUpperCase(String s);
}
PrinterDemo测试类:
public class PrinterDemo {
public static void main(String[] args) {
//在主方法中调用usePrinter方法
// usePrinter(s -> {
String result = s.toUpperCase();
System.out.println(result);
// System.out.println(s.toUpperCase());
// });
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
//Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
}
private static void usePrinter(Printer p) {
p.printUpperCase("HelloWorld");
}
}
5.6 引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
- 格式:类名::成员方法
- 范例:String::toUpperCase
- String类的方法:public String substring(int beginIndex,int endIndex)
- 从beginIndex开始,到endIndex结束,截取字符串。返回一个新的字符串,长度为endIndex-beginIndex
练习:
- 定义一个接口(MyString),里面定义一个抽象方法
- String muSubString(String s,int x,int y);
- 定义一个测试类(MyStringDemo),在测试类中提供两个方法
- 一个方法是:useMyString(MyString my)
- 一个方法是主方法,在主方法中调用useMyString方法
MyString接口:
public interface MyString {
String muSubString(String s,int x,int y);
}
MyStringDemo测试类:
public class MyStringDemo {
public static void main(String[] args) {
//在主方法中调用useMyString方法
useMyString((s, x, y) -> s.substring(x, y));//llo
//引用类的实例方法
useMyString(String::substring);//llo
//Lambda表达式被类的实例方法替代的时候
//第一个参数作为调用者
//后面参数全部传递给该方法作为参数
}
private static void useMyString(MyString my) {
String s = my.muSubString("helloworld", 2, 5);
System.out.println(s);
}
}
5.7 引用构造器
引用构造器,其实就是引用类中的构造方法
- 格式:类名::new
- 范例:Student::new
练习:
- 定义一个类(Student),里面定义两个成员变量(name,age)
- 并提供无参和带参构造方法,以及成员变量对应的set和get方法
- 定义一个接口(StudentBuilder),里面定义一个抽象方法
- Student build(String name,int age);
- 定义一个测试类(StudentDemo),在测试类中提供两个方法
- 一个方法是:useStudentBuilder(StudentBuilder s)
- 一个方法是主方法,在主方法中调用useStudentBuilder方法
Student类:
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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; }
}
StudentBuilder接口:
public interface StudentBuilder {
Student build(String name,int age);
}
StudentDemo测试类:
public class StudentDemo {
public static void main(String[] args) {
//在主方法中调用useStudentBuilder方法
// useStudentBuilder((name,age) -> {
// return new Student(name,age);
// });
useStudentBuilder(((name, age) -> new Student(name, age)));//林青霞,30
//引用构造器
useStudentBuilder(Student::new);//林青霞,30
//Lambda表达式被构造器替代的时候,它的形式参数全部传递给该方法作为参数
}
private static void useStudentBuilder(StudentBuilder s) {
Student student = s.build("林青霞", 30);
System.out.println(student.getName() + "," + student.getAge());
}
}