先赞后看,养成习惯

文本已收录至GitHub开源仓库 Lu_JavaNodes 码云仓库地址Lu_JavaNodes,包含教程涉及所有思维导图,案例代码和后续讲解视频,欢迎Star增砖添瓦。

前言

在传统的接口语法中,接口中只可以有抽象方法。在是在实际的使用中,我们往往会需要用到很多和接口相关的功能(方法),这些功能会单独的拿出开放在工具类中。

工具类:类中所有的方法都是静态的

例如:Collection 和 Collocations,Collection 是一个集合接口,而我们需要很多集合相关的操作,像集合的排序,搜索等等, 这时候人们会把这些静态方法放在 Collections 工具类中。

在传统Java中我们经常会看到这样的情况,有一个接口叫 A,这时候就会有一个类叫 As,As中全是和A接口有关的静态方法。
例如:Executor 和 Executors

这样的一种方式总归来说是有点不方便。于是在JDK8中Java对于接口做了一些改动,允许将静态方法直接写入接口中。(接口中可以定义静态方法,静态方法肯定不是抽象的,是有实现的)。

接口的静态方法

代码案例

根据上述内容,我们来定义一个接口,在接口中写入一个静态方法。

public class TestStaticInterface {

    public static void main(String[] args) {
//        静态方法可以通过类名直接调用  接口可以说是特殊的类 所以通过接口名可以调用接口中的静态方法
        HelloInterface.printHello();
    } } interface HelloInterface{
    int hhh(); //    定义静态方法
    static void printHello(){
        System.out.println("Hello");
    }
}

运行代码可以看到如下结果

静态方法有什么用呢?

静态方法实际上是很实用的,最基本的用法:我们可以把产生接口对象的方法放在接口中。

什么意思???好,接下来我们通过代码演示一下。

假设现在我们有一个 Animal 接口,那么这时候如果要获得一个Animal类型的对象,我们要怎么做呢?

传统方法,创建一个Animals工具类,在其中有一个 static Animal createDog()可以获取一个Animal类型的对象,代码如下

public class TestStaticInterface {

    public static void main(String[] args) {
//        通过工具类获取对象
        Animal animal = Animals.createDog();
    }
} class Animals{
    //    静态方法获取对象
    static Animal createDog(){
//        局部内部类
        class Dog implements Animal{         }
//        返回对象
        return new Dog();
    }
}

但是当你拥抱JDK8的时候,一切都不一样了,因为有接口静态方法,可以直接将接口对象的获取放在接口的静态方法中。代码如下

public class TestStaticInterface {

    public static void main(String[] args) {
//        通过接口的静态方法获取一个Animal类型的对象
        Animal animal = Animal.createDog();
    }
} interface Animal{
//    静态方法获取对象
    static Animal createDog(){
//        局部内部类
        class Dog implements Animal{         }
//        返回对象
        return new Dog();
    }
}

在JDK 的 API 中是怎么使用静态方法的

接下来我们通过Java中的API来验证一下这种使用方法。通过API文档,可以找到 Comparator 接口(比较器),在这个接口中现在就有很多的静态方法(JDK8)。如图

通过这些静态方法,就可以通过接口直接获取比较器对象。

public class TestStaticInterface {

    public static void main(String[] args) {
//        通过Comparator接口获取一个自然排序的比较器(自然排序就是String中默认实现的排序逻辑)
        Comparator<String> comparator = Comparator.naturalOrder();
//        创建集合
        List<String> list = Arrays.asList("b","a","c");
//        通过比较器对集合进行排序
        list.sort(comparator);         for (String s : list) {
            System.out.println(s);
        }
    }
}

传统接口的另一个问题:向后兼容性不好

现在接口已经有了静态方法,但是传统的接口还有另一个问题。我们举例说明:

假设你正在公司中做项目,在你的代码中,有一个UserService的接口,接口中有一个方法String getUsernameById()

interface UserService{
    String getUsernameById();
}

该接口因为在项目中存在老长时间了,所以实现类众多,有100个实现类。

one day,领导希望你给这个接口中添加一个新的接口方法String getIdByUsername()。这样的需求意味着要修改100个实现类,不要说写代码了,删库跑路的心都有了。

这是一个极端的案例,但是说明了一个事儿,传统的接口向后兼容性不好,不易于维护和改造

而这个问题,在JDK8中得到了解决,解决方法就是:接口的默认方法

接口的默认方法

Java 8 中允许接口中包含具有具体实现的方法,该方法称为 “默认方法”,默认方法使用 default 关键字修饰

在接口中使用 default 表示这个方法有实现,接口中所有的方法都是 public

示例代码

interface UserService{
    String getUsernameById(); //    默认方法
    default void m1(){
        System.out.println("这是一个默认方法");
    }
} class UserServiceImpl implements UserService{     @Override
    public String getUsernameById() {
        return null;
    }
}

示例代码的问题

看了这样的一段代码,你一定会有一些疑问,我们一起来解决一下。

接口中的默认方法,实现类能不能继承到

答:这个当然是可以的,并且在实现类中依然可以进行方法的覆盖。

如果 UserServiceImpl 再有一个父类,父类中也有m1方法,那么UserServiceImpl 继承到的是父类还是接口中的m1方法

interface UserService{
    String getUsernameById(); //    默认方法
    default void m1(){
        System.out.println("这是一个默认方法");
    }
} //父类
class UserSer{
    public void m1(){
        System.out.println("这是一个默认方法");
    }
} class UserServiceImpl extends UserSer implements UserService{     @Override
    public String getUsernameById() {
        return null;
    }
}

答:在实现类中继承到的是父类中的。因为接口默认方法有”类优先”的原则

接口默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中 又定义了一个同名的方法时

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么
    接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接 口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

对于 JDK8 接口新语法的思考

关于接口新语法的讲解实际上已经结束了,但是想要和大家一起延伸一下思考,看下面一个案例。

interface IA{
    default void m2(){
        System.out.println("IA");
    }
} interface IB{
    default void m2(){
        System.out.println("IB");
    }
} class ImplC implements IA,IB{
//    接口冲突 通过覆盖解决
    public void m2(){
        System.out.println("Impl");
    }
}

以上代码实际上就是 “类优先”原则第二条接口冲突的演示代码,而我要思考的问题不是这个,而是:1.在实现类中,如何使用super,2.如果IA 和 IB 接口中的m2方法返回值不同怎么办?

1.在实现类中,如何使用super?

第一个问题,比较好解决,因为有m2来自两个接口,所以我们如果要调用super的话,需要说明要调用那个接口的super,语法:接口名.super.m2()

实现类继承的方法来自两个接口,必须覆盖,否则引用不明确。要调用super,也必须指明要调用那个接口。

其实这个问题来自多继承,过去接口比较简单,调用 super肯定不会调用接口,接口中方法都是抽象的,现在不一样了,父类和接口中都有方法实现,这时候再要调用就要指明要调用谁了。

虽然Java一直都是单继承,但是这个语法实际上已经是向多继承靠近了。只不过并没有把多继承正式的引入Java,所以会有一定的不足,这就是我们的第二个思考题。

2.如果IA 和 IB 接口中的m2方法返回值不同怎么办?

这其实也是一个标准的多继承的问题,在现版本没有解决。

在C++中其实就简单了,可以指定要覆盖谁

总结

学过了接口的静态方法和默认方法,仿佛发现了一个事儿,接口和抽象类越来越像了,那么这时候再问你那个问题:接口和抽象类有什么区别?

这个问题留给大家,好像以前背答案开始不好使了。

最后我们简单总结一下JDK8接口语法的新变化:在JDK8以后的接口中,允许有静态方法和默认方法(default)修饰

求关注,求点赞,求转发

欢迎关注本人公众号:鹿老师的Java笔记,将在长期更新Java技术图文教程和视频教程,Java学习经验,Java面试经验以及Java实战开发经验。

什么?接口中方法可以不是抽象的「JDK8接口新语法的深度思考」的更多相关文章

  1. 关于Override在JDK1.5和JDK1.6上子类实现接口中方法使用@Override注解编译错误.

    遇到这个问题说来也怪.新开了一个path的工作空间用来打patch.该Eclipse的默认全局的编译版本是1.6.但是唯独其中的一个插件项目的版本是1.5(可能是唯一的一个,不确定,不知道为什么会是这 ...

  2. java中抽象类与接口中方法访问修饰符问题 (

    1.抽象类中的抽象方法(其前有abstract修饰)不能用private.static.synchronized.native访问修饰符修饰.原 因如下:抽象方法没有方法体,是用来被继承的,所以不能用 ...

  3. Collection接口中方法的使用

    Collection:集合的接口 1.Collection和ArrayList和List的关系 ArrayList      implement(实现)       List List        ...

  4. java lambda怎么表达式判断被调用接口名称和接口中方法

    1.首先能够用于lambda表达式的只能是interface,并且interface 中只有一个方法. 这就说明,只要找到接口类型就能确定用的是哪个方法.(如下:intTypeInterface.St ...

  5. 接口中字段的修饰符:public static final(默认不写) 接口中方法的修饰符:public abstract(默认不写)abstract只能修饰类和方法 不能修饰字段

    abstract只能修饰类和方法 不能修饰字段

  6. Java接口中的方法

    接口中可以含有变量和方法.但是,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法 ...

  7. 教你在Java接口中定义方法

    基本上所有的Java教程都会告诉我们Java接口的方法都是public.abstract类型的,没有方法体的. 但是在JDK8里面,你是可以突破这个界限的哦. 假设我们现在有一个接口:TimeClie ...

  8. java 接口中的成员变量与方法

    java接口中变量的默认修饰符为 public static final int i = 3; 相当于 public static final int i = 3; java接口中方法的默认修饰符为 ...

  9. 纳尼,java可以在接口中实现非抽象方法了?

    纳尼,接口中可以定义实例方法了?! 纳尼,接口中还可以定义静态方法了?! 没错,在Java8中新增了很多新的特性,其中就包括可以在接口中添加方法和变量. 首先我们来看下代码 public interf ...

随机推荐

  1. cogs 1176. [郑州101中学] 月考 字典树

    1176. [郑州101中学] 月考 ★★☆   输入文件:mtest.in   输出文件:mtest.out   简单对比时间限制:1 s   内存限制:128 MB [题目描述] 在上次的月考中B ...

  2. rdlc报表输入中文出现小方块

    在用vs自带的报表文件的时候,在输入中文的时候,会出现一些小方块. 百度到的资料:当然我试了下,没有用. 用文本编辑器(我用的是editplus)打开需要批量处理的rdlc文件. 将所有 <St ...

  3. 关于neo4j初入门(5)

    neo4j和Java Neo4j提供JAVA API以编程方式执行所有数据库操作. 它支持两种类型的API: Neo4j的原生的Java API Neo4j Cypher Java API Neo4j ...

  4. css 脱离文档流

    一.float <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset=&quo ...

  5. Qt Installer Framework翻译(3-3)

    移除组件 下图说明了删除所有或某些已安装组件的默认工作流程: 本节使用在macOS上运行的Qt 5维护工具为例,来演示用户如何删除所有或部分选定组件. 移除所有组件 用户启动维护工具时,将打开&quo ...

  6. 异数OS 织梦师-云(五)-- 容器服务化,绿色拯救未来。

    . 异数OS 织梦师-云(五)– 容器服务化,绿色拯救未来. 本文来自异数OS社区 github: https://github.com/yds086/HereticOS 异数OS社区QQ群: 652 ...

  7. 异数OS 织梦师-水桶(三)-- RAM共享存储方案

    . 异数OS 织梦师-水桶(三)– RAM共享存储方案 本文来自异数OS社区 github: https://github.com/yds086/HereticOS 异数OS社区QQ群: 652455 ...

  8. [bzoj3938] [Uoj #88] Robot

    Description 小 \(q\) 有 \(n\) 只机器人,一开始他把机器人放在了一条数轴上,第 \(i\) 只机器人在 \(a_i\) 的位置上静止,而自己站在原点.在这之后小 \(q\) 会 ...

  9. set(待整理)

    set集合容器:实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调整二叉树的排列,把元素放到适当的位置,以保证每个子树根节点键值大于左子树所有节点的键值,小于右子树所有节点的键值:另外,还 ...

  10. P2869 [USACO07DEC]美食的食草动物Gourmet Grazers

    P2869 [USACO07DEC]美食的食草动物Gourmet Grazers 题目:约翰的奶牛对食物越来越挑剔了.现在,商店有M 份牧草可供出售,奶牛食量很大,每份牧草仅能供一头奶牛食用.第i 份 ...