JAVA基础之三-接口和抽象类

java提供了抽象类和接口,总体是好事。

有的OOP语言并没有接口的概念,但相当一部分其实用其它方式实现了JAVA中接口类似的功能。

如果不太清楚二者的区别,难免在面临具体业务的时候,在二者之间摇摆。

---

实际上,关于抽象类和接口的共同点和不同点没有什么可以写的。

设计原则原则让我们尽量基于接口编程(IOP-Interface-Oriented Programming),而不是基于具体类,所以多用接口。

如果需要继承(通常就存在属性),则多用抽象类。

---

一、基本概念

这里有个难点:java的抽象类和接口已经不如发明者期望的那么纯粹了。

所以,以下的概念主要适用于J17及其以上版本。

2.1、抽象类

核心概念:无法实例化的类

用处:为子类提供模板,限定子类的实现,并为子类提供一些公共的且已经实现的方法/属性。

可以包含内容:各种可见范围的都可以,最典型的就是抽象方法。除了不能用default,好像就么有不行的。

如果只定义了抽象方法,那么它的作用基本等同于接口。

2.2、接口

核心概念:必须被实现的类,否则无用(但实际不是那么回事,自从J9之后可以工具化)

用处:抽象以实现工程上的多态,从而实现模块话;为函数式编程服务

可以包含内容:除了不能定义只包含public修饰符的方法,好像都可以。

二、比较

总体来说,java的抽象类和接口越发地同化了,除了以下几个不能互相替代,好像没有不能互换的:

a.抽象类的可以实现各种修饰符的方法或者属性   -- 接口做不到,至少接口不能定义只有public修饰符的方法

b.接口可以用于实现函数式编程;接口可以用于实现注解

 

抽象类更适合于定义带有属性的类,接口更适合用于只有实现方法的类(虽然可以带静态属性)。

因为接口的这个特性,所以,接口很适合用于定义spring中bean,因为我们用bean的最主要目的就是为了实现单例的bean,

我们通常不关注特定的bean示例到底有什么特性,只像知道某个bean示例能干什么。

所以,java这种和spring结合很深的语言,接口有大用处。

 

当然,在spring中,如果bean继承抽象类来是实现bean的定义和注入也是可以的,不过这个时候,就不要为抽象类定义什么属性了。

以下就是这个演示(已经省略了包和import部分):

package study.base.oop.classes.modifier.abstractbean;

/**
 * 抽象类,用于演示基于抽象类的 bean
 */
public abstract class AbstractLiterature {
    
    public abstract String writePoem();
}
 
@Component
public class ChineseLiteratureService extends AbstractLiterature {

    @Override
    public String writePoem() {
        return """
                两个黄鹂鸣翠柳,
                一行白鹭上青天。
                醉里挑灯看剑,梦回吹角连营.八百里分麾下炙人,五十先翻塞外声,沙场秋点兵。                            
                """;
    }

}

@RequestMapping("/spring/bean/")
@Controller
public class BeanController {

    @Autowired
    private AbstractLiterature literatureService;
    
    @GetMapping("/abstract-bean")
    @ResponseBody
    public Object testAbstractBean() {
        String poem= this.literatureService.writePoem();
        return poem;
    }    
}

 

 编译不会报错,结果也可以看到:

所以,用接口还是用抽象类来接口定义,真是一个问题。

 

三、适用场景

3.1 想想要不要个性化的属性

如前,如果想保留自有属性,那么用抽象类更好一些;如果不需要保留自有属性,用接口更好一些。

 

实现一个bean的时候为什么都是基本是实现一个接口,而不是扩展一个抽象类?

也许仅仅处于两个考虑:

1.即使偏好抽象类,也需要避免有人往抽象类中添加属性,变成不稳定的bean

2.仅仅基于一个朴素的想法:单例bean还要什么属性,所以用理论山干净一点的接口更好一些。

因为第2个因素的缘故,所以我们都会再三强调,不要在bean中定义可变的属性/成员,即使要定义了,我们也是为了用这些

成员的能力而不是成员的数据。这样做的最重要的目的是为了保证获得方法的某种程度的幂等性。

一旦实现类的属性(作为数据)参与方法计算,那么由于单例的特性,可能会照成难于预料的结果,通常那不是我们期望

的结果。

为什么单例bean的属性会被共享? 这和JVM的内存模型有不可分割的关系。

 

 

3.2 接口特有的

至于注解和函数式编程,现在只能求助于接口。

如果要实现多继承,现在也只能求助于接口。

3.3默认使用接口的

具体到JAVA的经典应用场景-开发信息应用系统后台,那么还是偏多使用接口。这是因为现在javaEE基本和Spring深度绑定,

spring和Bean深度绑定,Bean基本上就是单例bean,单例意味着不要特性化的属性,不要属性,那么用接口无疑是最好不过了。

 

3.4 不用bean的地方-也许抽象类可以排上用场

当我们不用bean的时候,通常意味着要写一些相对复杂一些的功能,这个时候抽象类可能更加方便一些。

如果需要定义公共的描述,并基于这个定义构建大模块,可以考虑用抽象类,或者普通的类也可以。

 

四、小结

虽然随着JAVA版本的变更,接口和抽象类越发相像。

让接口专享注解和函数式编程,使得接口显得有点独大。

不过当你不想单例的时候,应该更多考虑抽象类。