接口和抽象类是面向对象编程中常用的概念,它们在软件设计中起着重要的作用。
接口是一种纯粹的规范,用于定义类之间的契约。它只包含方法的定义,没有具体的实现。接口的作用包括:
接口是对行为的抽象,应该先把要干什么事定下来,然后依据接口隔离原则封装成接口。为了避免让子类实现不需要的方法,接口的功能就应该单一,而不是大而全。
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。 ——接口隔离原则
这样就足够了,其实设计接口不太难,因为接口和实现类耦合度较低,即实现类主体是什么、如何实现的,接口并不关心,只需要保证设计的方法能够充分让实现类做自己想做的事。
只需要让实现类做到继承一个接口就好像插上一件特定功能的装备,需要什么功能就继承什么接口。
展开来说有以下几个点需要注意:
从Java 8开始,接口引入了一些新的接口特性,这些特性让接口在功能和灵活性上得到了增强。
public interface Vehicle {
void start();
default void stop() {
System.out.println("Vehicle stopped.");
}
}
public class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car started.");
}
}
public class Main {
public static void main(String[] args) {
Vehicle car = new Car();
car.start(); // 输出:Car started.
car.stop(); // 输出:Vehicle stopped.
}
}
public interface MathUtils {
static int add(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
int sum = MathUtils.add(3, 5);
System.out.println("Sum: " + sum); // 输出:Sum: 8
}
}
public interface Calculator {
default int add(int a, int b) {
return addNumbers(a, b); //只允许内部调用
}
private int addNumbers(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator() {};
int sum = calculator.add(3, 5);
System.out.println("Sum: " + sum); // 输出:Sum: 8
}
}
抽象类是一个类,包含抽象方法的类叫做抽象类,它自己也可以有具体的方法实现和属性定义,子类可以直接复用这些代码。普通类和抽象类相比只少了抽象方法。
设计抽象类的难度比设计接口大,因为抽象类本质是类,包含子类的通用特性,接口是行为的抽象,抽象类内容上比接口复杂,功能上比接口强大。
抽象类和它的子类关系紧密,耦合度非常高。设计抽象类是一个自底向上的过程,应该先有子类,再根据子类的共性抽象出抽象类。
设计应考虑按照以下步骤:
职责链模式就使用了抽象类,因为它既要提供抽象的处理方法让子类实现,也要提供存储下一链的属性,所以使用了抽象类而不是接口。
public abstract class RuleHandler {
protected RuleHandler successor;
/**
* 设置规则职责链继任者
*/
public void setSuccessor (RuleHandler successor){
this.successor = successor;
}
/**
* 处理请求的抽象方法
*/
public abstract ResponsibilityChainParameterModel handleRequest(ResponsibilityChainParameterModel parameterModel) ;
}
这要依据业务场景而定。
以下几个情况时,可以使用接口:
以下情况可以使用抽象类:
当有一组类具有重复的方法和属性,应抽象出一个父类,方便管理子类的行为规范,实现代码的复用。如果该父类有些方法没有默认实现,只有抽象概念,则转为抽象类。
其实不能,抽象类的抽象方法规定子类必须实现,这样的强制行为规范是普通父类做不到的。另外还有一个区别是抽象类不能被实例化,普通父类可以。
默认方法的意义:
在接口中添加默认方法不会破坏现有的实现类,因为实现类不需要强制实现该方法。这样就可以在接口中演化和扩展新的功能,而不会影响已有的代码。
静态方法的意义:
静态方法可以在接口中定义一些独立于实例的方法,无需创建接口的实例就可以直接使用。这样可以避免在某些情况下创建实例的开销。并且扩展功能也不会影响已有的代码。
私有方法的意义:
代码重用和封装:私有方法可以在接口内部封装一些共享的代码逻辑,供默认方法调用。这样可以避免代码的重复编写,提高代码的可维护性和可读性。
内部实现细节隐藏:私有方法只能在接口内部被调用,对于接口的使用者来说是不可见的。这样可以隐藏接口的内部实现细节,只向外部暴露必要的方法。
直接用抽象类不行。java中是单继承,只能继承一个类,使用抽象类一定会让类之间耦合关系紧密,但是接口可以实现多个,耦合关系较低。
基础是非常重要的,这方面的思考不能少,有兴趣的可以在评论区发表见解,我们一起讨论。