几周前,我写了一个博客,说开发人员学习新语言是因为它们很酷。 我仍然坚持这个主张,因为关于Java 8的事情真的很酷。 毫无疑问,该节目的明星是添加了Lambdas以及将函数提升为一等变量,而我目前最喜欢的是默认方法。 这是因为它们是在不破坏旧代码的情况下向现有接口添加新功能的一种巧妙方法。

实现很简单:采用一个接口,添加一个具体方法,并将关键字default附加为修饰符。 结果是,接口的所有现有实现突然都可以使用此代码。 在第一个简单示例中,我添加了默认方法,该方法返回接口1的版本号。

public interface Version { /** * Normal method - any old interface method: * * @return Return the implementing class's version */ public String version(); /** * Default method example. * * @return Return the version of this interface */ default String interfaceVersion() { return "1.0"; } }

然后,您可以在任何实现类上调用此方法。

public class VersionImpl implements Version { @Override public String version() { return "My Version Impl"; }
}

您可能会问:为什么这很酷? 如果采用java.lang.Iterable接口并添加以下默认方法,则会使for循环失效。

default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }

forEach方法采用实现Consumer<T>接口作为参数的类的实例。 Consumer<T>可以在新的java.util.function包中找到,它是Java 8所谓的功能接口 ,该接口仅包含一个方法。 在这种情况下,方法accept(T t)接受一个参数并且返回一个void

java.util.function软件包可能是Java 8中最重要的软件包之一。它包含一堆描述通用函数类型的单一方法或函数接口。 例如, Consumer<T>包含一个接受一个参数并返回void的函数,而Predicate<T>是一个包含一个接受一个参数并返回boolean的函数的接口,通常用于编写过滤lambda。

该接口的实现应包含您先前在for循环括号之间编写的内容。

那么,您可能会想,这给了我什么? 如果不是Java 8,那么答案是“不多”。 要在Java 8之前使用forEach(…)方法,您需要编写如下代码:

List<String> list = Arrays.asList(new String[] { "A", "FirsT", "DefaulT", "LisT" }); System.out.println("Java 6 version - anonymous class"); Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String t) { System.out.println(t); } }; list.forEach(consumer);

但是,如果将其与lambda表达式或方法引用结合使用,则可以编写一些看起来很酷的代码。 使用方法引用,前面的示例变为:

list.forEach(System.out::println);

您可以使用lambda表达式执行相同的操作:

list.forEach((t) -> System.out.println(t));

所有这些似乎都与Java 8背后的一个重要思想保持一致:让JDK为您完成工作。 用政治家和连环友约翰·肯尼迪(John F Kennedy)的话来形容“不要问您对JDK可以做什么,请问您的JDK可以为您做什么” 2

默认方法的设计问题

那是编写无处不在的for循环的一种很酷的新方法,但是在接口中添加默认方法是否存在问题?如果是的话,它们是什么?Java 8项目中的人如何修复它们?

首先要考虑的是继承。 当您拥有一个扩展了另一个接口的接口并且两个接口都具有带有相同签名的默认方法时,会发生什么? 例如,如果您拥有由MiddleInterface扩展的SubInterface和由SuperInterface扩展的MiddleInterfaceSubInterface怎么SubInterface

public interface SuperInterface { default void printName() { System.out.println("SUPERINTERFACE"); }
}
public interface MiddleInterface extends SuperInterface { @Override default void printName() { System.out.println("MIDDLEINTERFACE"); }
}
public interface SubInterface extends MiddleInterface { @Override default void printName() { System.out.println("SUBINTERFACE"); }
}
public class Implementation implements SubInterface { public void anyOldMethod() { // Do something here } public static void main(String[] args) { SubInterface sub = new Implementation(); sub.printName(); MiddleInterface middle = new Implementation(); middle.printName(); SuperInterface sup = new Implementation(); sup.printName(); }
}

无论用哪种方式剪切, printName()都将始终打印“ SUBINTERFACE”。

当您具有包含相同方法签名的类和接口时,会出现相同的问题:哪个方法在运行? 答案是“阶级胜利”法则。 接口默认方法将始终被类方法所忽略。

public interface AnyInterface { default String someMethod() { return "This is the interface"; }
}
public class AnyClass implements AnyInterface { @Override public String someMethod() { return "This is the class - WINNING"; } }

运行上面的代码将始终打印出:“这是课程-WINNING”

最后,如果一个类实现两个接口并且都包含具有相同签名的方法,会发生什么? 这是古老的C ++钻石问题 ; 您如何解决歧义? 运行哪种方法?

public interface SuperInterface { default void printName() { System.out.println("SUPERINTERFACE"); }
}
public interface AnotherSuperInterface { default void printName() { System.out.println("ANOTHERSUPERINTERFACE"); }
}

在Java 8的情况下,答案都不是。 如果您尝试同时实现这两个接口,则会收到以下错误:

Duplicate default methods named printName with the parameters () and () are inherited from the types AnotherSuperInterface and SuperInterface.

在绝对必须实现两个接口的情况下,解决方案是调用“类获胜”规则并覆盖实现中的歧义方法。

public class Diamond implements SuperInterface, AnotherSuperInterface { /** Added to resolve ambiguity */ @Override public void printName() { System.out.println("CLASS WINS"); } public static void main(String[] args) { Diamond instance = new Diamond(); instance.printName(); } }

何时使用默认方法

从纯粹的角度来看,默认方法的添加意味着Java接口不再是接口。 接口被设计为用于拟议/预期行为的规范或合同:实施类必须履行的合同。 添加默认方法意味着接口和抽象基类之间实际上没有区别3 。 这意味着他们容易受到滥用,因为一些经验不足的开发人员可能认为从其代码库中删除基类并用基于默认方法的接口替换它们很酷–只是因为它们可以,而其他人可能只是将抽象类与实现默认值的接口混淆了方法。 我目前建议仅将默认方法用于其预期的使用情况:在不破坏现有代码的情况下改进传统接口。 虽然我可能会改变主意。

1它不是很有用,但是它说明了一点……

2肯尼迪(John F Kennedy)的就职演说1961年1月20日。

3抽象基类可以具有构造函数,而接口则不能。 类可以具有私有实例变量(即状态)。 接口不能。

翻译自: https://www.javacodegeeks.com/2014/08/default-methods-java-8s-unsung-heros.html

默认方法:Java 8的无名英雄相关推荐

  1. Java 8 新特性之默认方法

    1. 简介   默认方法就是接口可以有实现方法, 而且不需要实现类去实现其方法, 默认方法的实现只需要在方法名的前面加一个default关键字即可实现默认方法. 2. 默认方法语法 public in ...

  2. viewpager默认界面_使用默认方法的界面演变–第一部分:方法

    viewpager默认界面 几周前,我们详细研究了默认方法 -Java 8中引入的一项功能,该功能允许为接口方法提供实现,即方法主体,从而定义接口中的行为. 引入此功能是为了实现接口演进 . 在JDK ...

  3. 使用默认方法的界面演变–第一部分:方法

    几周前,我们详细研究了默认方法 -Java 8中引入的一项功能,该功能允许为接口方法提供实现,即方法主体,从而定义接口中的行为. 引入此功能是为了实现接口演进 . 在JDK的上下文中,这意味着在不破坏 ...

  4. java8接口写静态方法_Java 8接口更改–静态方法,默认方法

    java8接口写静态方法 Java 8 interface changes include static methods and default methods in interfaces. Prio ...

  5. java8 默认方法_默认方法:Java 8的无名英雄

    java8 默认方法 几周前,我写了一个博客,说开发人员学习新语言是因为它们很酷. 我仍然坚持这个主张,因为关于Java 8的事情真的很酷. 尽管毫无疑问,该节目的明星是添加了Lambdas和将函数提 ...

  6. Java 8默认方法会破坏你的(用户的)代码

    Java 8的默认方法试图尝试更进一步简化Java API.不幸的是,这一最近的语言扩展带来了一系列复杂的规则,但只有少部分Java开发者意识到这一点.这篇文章告诉你为什么引入默认方法会破坏你的(用户 ...

  7. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  8. Java 8 (8) 默认方法

    传统上,Java程序的接口是将相关方法按照预定组合到一起的方式.实现接口的类必须为接口中定义的方法提供一个实现,或者从父类中集成它的实现.但是,一旦类库的设计者需要更新接口,向接口中加入新的方法时候, ...

  9. JAVA移慎_谨慎使用Java8的默认方法

    为什么要谨慎使用Java8的默认方法?本文给出了为什么要慎用Java8默认方法的原因,解释的很详细,感兴趣的朋友可以参考一下 默认方法给JVM的指令集增加了一个非常不错的新特性.使用了默认方法之后,如 ...

最新文章

  1. python中文读音ndarray-numpy中的ndarray方法和属性详解
  2. Kylin的cube模型
  3. 深度学习打造精准推荐系统,细说国美互联网AI发展的进击之路
  4. 弹出提示对话框并重定向网页
  5. Mongodb~Linux环境下的部署~服务的部署与自动化
  6. golang中go mod使用第三方包
  7. Android单元测试 - 几个重要问题
  8. php聊天室技术,PHP聊天室技术
  9. LinuxQuestions.org庆祝16岁生日
  10. PYTHON 笔记:函数的参数(关键字参数,默认参数,可变长参数,可变长的关键字参数)
  11. 拓端tecdat|R语言GGPLOT2绘制KOLMOGOROV-SMIRNOV KS检验图ECDF经验累积分布函数曲线可视化
  12. java怎么定义_java怎么定义方法
  13. 如何安装Scrapy
  14. 软件测试的测试方法及测试流程
  15. 基于C#和遥感软件二次开发的宜居度综合方法
  16. 栈判断字符串是否为中心对称_数据结构 Stacks 栈
  17. Ubuntu20.4:安装OpenCV4,配置vscode+CMake作为基本开发环境
  18. linux加静态路由命令,LINUX添加静态路由
  19. 用爬虫模拟登陆urp教务处系统
  20. stm32f103开发板入门到手进行开发

热门文章

  1. 150. 逆波兰表达式求值---JAVA---LeetCode
  2. String中的compareTo()方法
  3. 脚本可以放在html外,关于把script脚本放在html结束标签外的运行结果???
  4. 日期相减 python_如果将excel的数字转化为日期(高级教程)
  5. 三级pc技术_第十九周PC、笔电、数码周边新品汇总:AMD英特尔激战正酣
  6. mybatis-启动源码分析
  7. React学习途径和资料分享
  8. javafx 打开新窗口_新的JMetro JavaFX 11兼容版本
  9. spring api层打包_Spring项目的按层打包已过时
  10. 结果集 tostring_关于避免对toString()结果进行解析或基于逻辑的美德