接口中的方法到底能有具体实现吗?

探究一个问题:接口中的方法到底能有具体实现吗?

发现问题

这是我无意间发现的一个问题,有一天在写代码的时候突然发现,我实现了一个接口却没有将他的抽象方法重写完,这还不是问题,问题是他还不报错。这是怎么回事呢?按理说接口的定义不就是:一个类实现该接口就必须重写其所有方法吗,为啥我没有把他的抽象方法重写完,还不报错呢?难道是编译器出错了?

寻找答案

于是我查了很多资料,发现:原来接口中的方法也是能有具体的实现的,这种改动源自于java8(我们口中所说的java8,JDK8,JDK1.8其实都是同一个东西,这里我就这样叫了)。也就是说在java8之前是没有这种语法的。看来,要想在IT一行干的好,必须要与时俱进啊。

具体原因

自从 java8开始就允许了接口里的方法有具体的实现,这个方法包含:默认方法和静态方法。这两个种方法都有具体的应用场景

默认方法的应用场景

我们先回忆一下接口的定义:接口里的方法默认都是public abstract修饰的

因此接口里的方法都是抽象方法,但是看完我的讲解,你会有不一样的发现

场景一:

我们都知道一个软件在使用的过程中免不了要更新,这种更新有助于拓展新的功能、修复bug等。当然JDK也不例外,当用户在使用过程中遇到什么更加复杂的场景,那么JDK的开发团队就会想办法新增一些功能来应对这些场景。但是如果直接在我们原先定义好的接口上新增的话,那么实现这个接口的所有类必须要重写这个接口的所有方法。如果是这样,那些在公司中已经在运转的项目难道要停一段时间,先把方法给重写了吗?这显然是不现实的,如果要是这样搞,那谁还想继续升级JDK啊,升级一次就迎来一次经济损失。况且,即使重写了方法也是个空的实现,根本用不到(此时还用不到这新增的功能)。这就体现了默认方法的重要性。

总结来说就是:默认方法提供了一种在接口中添加新方法的方式,而不会破坏已有的实现类,这样做的好处就是:我们可以向接口中添加新的功能,但不会破坏已有的代码。

场景二:

每当我们实现一个接口的时候,总要重写这个接口的所有抽象方法,但是有一种可能:我们不需要将所有的方法重写完就能完成我们的业务。此时默认方法的作用也就体现出来了,新增默认方法没有重写的负担,也就是在后来实现这个接口的时候不用重写默认方法,但是可以直接调用,这也是接口里的方法具体化的好处,还能减少了代码的无用逻辑。

默认方法的使用

我们可以看到:Cat直接实现Animal但不重写bark方法时,如果接口里的方法是默认方法(带有default修饰),是不会报错的。

java 复制代码
public interface Animal {
    //这个方法就是默认方法
    default void bark(){
        System.out.println("动物会叫");
    }
}


public class Cat implements Animal{

}

加上public不会报错,但是如果我们将abstract加上会发生报错,说明此时的方法已经不是abstract修饰了,而是public default修饰,说到这里不知道你会不会有点懵,为啥default和public一起用了,这两个不是同一级别的吗,不都是修饰限定符吗?其实这里是个特殊的用法。这里有一些百度的解释,可以了解一下。

因此我们就不用重写,直接能调用我们继承的属性了

其实说到这,继承(extend)和实现(implement)的区别也想一起说说,

说实话,他们有联系又有区别。但总结来说:

  • 继承是:is a 的关系,就比如猫是动物,猫继承了动物的属性
  • 实现是:has a 的关系,一个猫实现了 " 跑 " 接口,就表明它拥有了跑这个功能
    那是概念上来说。如果放在代码层面,其实都能为我们原来的类新增一些功能或属性以便我们直接调用。
java 复制代码
public interface Animal {
     default void bark(){
        System.out.println("动物会叫");
    }
}

public class Cat implements Animal{
    public static void main(String[] args) {
        //这里我们没有重写bark方法,但是能打印出来结果,说明默认方法被调用了
        Cat cat = new Cat();
        cat.bark();
    }
}

运行结果

静态方法的应用场景

想一想如果此时有一个类继承了两个接口,但是两个接口同时含有重名的方法时,此时如果你不进行重写,那么你实例化以后调用的到底是哪一个bark?可以看到IDEA都报错了,所以我们是不会知道到底会调用哪一个方法的,这就体现了静态方法的重要性。下面有图:

静态方法:默认使用public static修饰,用于提供与接口相关的实用方法或工具方法。

java 复制代码
public interface Animal {
      default void bark(){
        System.out.println("动物会叫");
    }
}
public interface Creature {
    default void bark(){
        System.out.println("生物会叫");
    }
}
public class Cat implements Animal,Creature{
    public static void main(String[] args) {
        Cat cat =new Cat();
        cat.bark();
    }
}

静态方法的使用

此时我们将其中一个接口里的方法改成静态的,那么就不会发生报错,但是由于方法成了静态方法,就成了类的属性,而不是对象的属性了,所有我们使用new对象的方式是拿不到静态方法的,所以只能打印default方法的内容。

java 复制代码
public interface Animal {
    //我们将这里改成static
      static void bark(){
        System.out.println("动物会叫");
    }
}

public interface Creature {
    default void bark(){
        System.out.println("生物会叫");
    }
}
public class Cat implements Animal,Creature{
    public static void main(String[] args) {
        Cat cat =new Cat();
        cat.bark();
    }
}

虽然不能使用对象调用,但是我们可以通过 类名. 方法名的方式来调用

java 复制代码
public interface Animal {
      static void bark(){
        System.out.println("动物会叫");
    }
}

public interface Creature {
    default void bark(){
        System.out.println("生物会叫");
    }
}
public class Cat implements Animal,Creature{
    public static void main(String[] args) {
//        Cat cat =new Cat();
//        cat.bark();
        Animal.bark();
    }
}

运行结果:

这也大大方便了我们。

思维拓展(我们身边的实例)

不知道你有没有注意过一些接口,能用于集合排序的Collections接口和 Map接口

Collections接口的源码:

可以看到这个sort方法就是static修饰的,因此能直接使用类名.方法名调用,这也就是我们能直接调用sort方法的原因。

你说我之前直接用这个方法的时候怎么没有想这么多呢?知道他能排序,便感觉又掌握了一个新的方法,感觉自己又进步了,殊不知真正的进步是追根溯源。

Map接口源码:

可以看到forEach方法被default方法修饰的,所以我们在实现完这个接口以后不用重写forEach方法,只需要new对象调用就行了。

承接上文,我们知道了接口里的方法并不一定是public abstract修饰了

也能是 public default ,public static修饰。

说到最后,你应该对接口有了更加深入的了解了。

总的来说:

其实接口就是相当于一个框架一样,他把一些事物的统一属性给抽象出来了,主要功能就是为了让我们在写代码的时候不跑偏,如果按照这个框架写,那么你就不会跑偏,这也是前人给我们总结的经验啊,唉,为了我们操碎了心。

相关推荐
liO_Oil几秒前
(2024.9.19)在Python的虚拟环境中安装GDAL
开发语言·python·gdal安装
万物得其道者成15 分钟前
React Zustand状态管理库的使用
开发语言·javascript·ecmascript
学步_技术21 分钟前
Python编码系列—Python抽象工厂模式:构建复杂对象家族的蓝图
开发语言·python·抽象工厂模式
【D'accumulation】35 分钟前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
wn5311 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
试行1 小时前
Android实现自定义下拉列表绑定数据
android·java
茜茜西西CeCe1 小时前
移动技术开发:简单计算器界面
java·gitee·安卓·android-studio·移动技术开发·原生安卓开发
Hello-Mr.Wang1 小时前
vue3中开发引导页的方法
开发语言·前端·javascript
救救孩子把1 小时前
Java基础之IO流
java·开发语言
WG_171 小时前
C++多态
开发语言·c++·面试