0

0

Java 8 API 设计经验浅析

黄舟

黄舟

发布时间:2017-02-23 10:46:28

|

1447人浏览过

|

来源于php中文网

原创

本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

任何写java代码的人都是api设计师!无论编码者是否与他人共享代码,代码仍然被使用:要么其他人或他们自己使用,要么两者皆有。因此,对于所有的java开发人员来说,了解良好api设计的基础很重要。

一个好的api设计需要仔细思考和大量的经验。幸运的是,我们可以从其他更聪明的人,如ference mihaly——正是他的博客启发我写了这篇java 8 api附录——那里得到学习。在设计speedment api时,我们非常依赖于他列出的接口清单。(我建议大家不妨读一读他的指南。)

从一开始就做到这一点很重要,因为一旦api发布,就会成为使用api的人坚实的基石。正如joshua bloch曾经说过的:“公共api,就像钻石一样永恒久远。你有机会把它做正确的话,就应该竭尽全力去做。”

一个精心设计的api结合了两个世界的精华,既是坚实而精确的基石,又具有高度的实施灵活性,最终让api设计师和api使用者受益。

至于为什么要使用接口清单?正确地获取api(即定义java类集合的可见部分)比编写构成api背后实际工作的实现类要困难得多。它是一个真的很少有人掌握的艺术。使用接口清单允许读者避免最明显的错误,成为更好的程序员和节省大量的时间。

强烈建议api设计者将自己置于客户端代码的角度,并从简单性,易用性和一致性方面优化这个视图——而不是考虑实际的api实现。同时,他们应该尽量隐藏尽可能多的实现细节。

不要用返回Null来表示一个空值

可以证明,不一致的null处理(导致无处不在的NullPointerException)是历史上Java应用程序错误最大的唯一来源。一些开发人员将引入null概念当作是计算机科学领域犯的最糟糕的错误之一。幸运的是,减轻Java null处理问题的第一步是在Java 8中引入了Optional类。确保将返回值为空的方法返回Optional,而不是null。

这向API使用者清楚地表明了该方法可能返回值,也可能不返回值。不要因为性能原因的诱惑使用null而不使用Optional。反正Java 8的转义分析将优化掉大多数Optional对象。避免在参数和字段中使用Optional。

你可以这样写

public Optional getComment() {
    return Optional.ofNullable(comment);
}


而不要这样写

Presentations.AI
Presentations.AI

AI驱动创建令人惊叹的演示文稿

下载

立即学习Java免费学习笔记(深入)”;

public String getComment() {
    return comment; // comment is nullable
}

不要将数组作为API的传入参数和返回值

当Java 5中引入Enum概念时,出现了一个重大的API错误。我们都知道Enum类有一个名为values()的方法,用来返回所有Enum不同值的数组。现在,因为Java框架必须确保客户端代码不能更改Enum的值(例如,通过直接写入数组),因此必须得为每次调用value()方法生成内部数组的副本。

这导致了较差的性能和较差的客户端代码可用性。如果Enum返回一个不可修改的List,该List可以重用于每个调用,那么客户端代码可以访问更好和更有用的Enum值的模型。在一般情况下,如果API要返回一组元素,考虑公开Stream。这清楚地说明了结果是只读的(与具有set()方法的List相反)。

它还允许客户端代码容易地收集另一个数据结构中的元素或在运行中对它们进行操作。此外,API可以在元素变得可用时(例如,从文件,套接字或从数据库中拉入),延迟生成元素。同样,Java 8改进的转义分析将确保在Java堆上创建实际最少的对象。

也不要使用数组作为方法的输入参数,因为——除非创建数组的保护性副本——使得有可能另一个线程在方法执行期间修改数组的内容。

你可以这样写

public Stream comments() {
    return Stream.of(comments);
}


而不要这样写

立即学习Java免费学习笔记(深入)”;

public String[] comments() {
    return comments; // Exposes the backing array!
}

考虑添加静态接口方法以提供用于对象创建的单个入口点

避免允许客户端代码直接选择接口的实现类。允许客户端代码创建实现类直接创建了一个更直接的API和客户端代码的耦合。它还使得API的基本功能更强,因为现在我们必须保持所有的实现类,就像它们可以从外部观察到,而不仅仅只是提交到接口。

考虑添加静态接口方法,以允许客户端代码来创建(可能为专用的)实现接口的对象。例如,如果我们有一个接口Point有两个方法int x() 和int y() ,那么我们可以显示一个静态方法Point.of( int x,int y) ,产出接口的(隐藏)实现。

所以,如果x和y都为零,那么我们可以返回一个特殊的实现类PointOrigoImpl(没有x或y字段),否则我们返回另一个保存给定x和y值的类PointImpl。确保实现类位于另一个明显不是API一部分的另一个包中(例如,将Point接口放在com.company。product.shape中,将实现放在com.company.product.internal.shape中)。

你可以这样写

Point point = Point.of(1,2);


而不要这样写

立即学习Java免费学习笔记(深入)”;

Point point = new PointImpl(1,2);

青睐功能性接口和Lambdas的组合优于继承

出于好的原因,对于任何给定的Java类,只能有一个超类。此外,在API中展示抽象或基类应该由客户端代码继承,这是一个非常大和有问题的API 功能。避免API继承,而考虑提供静态接口方法,采用一个或多个lambda参数,并将那些给定的lambdas应用到默认的内部API实现类。

这也创造了一个更清晰的关注点分离。例如,并非继承公共API类AbstractReader和覆盖抽象的空的handleError(IOException ioe),我们最好是在Reader接口中公开静态方法或构造器,接口使用Consumer 并将其应用于内部的通用ReaderImpl。

你可以这样写

Reader reader = Reader.builder()
    .withErrorHandler(IOException::printStackTrace)
    .build();


而不要这样写

立即学习Java免费学习笔记(深入)”;

Reader reader = new AbstractReader() {
    @Override
    public void handleError(IOException ioe) {
        ioe. printStackTrace();
    }
};

确保你将@FunctionalInterface注解添加到功能性接口

使用@FunctionalInterface注解标记的接口,表示API用户可以使用lambda实现接口,并且还可以通过防止抽象方法随后被意外添加到API中来确保接口对于lambdas保持长期使用。

你可以这样写

@FunctionalInterface
public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods cannot be added
}


而不要这样写

立即学习Java免费学习笔记(深入)”;

public interface CircleSegmentConstructor {
    CircleSegment apply(Point cntr, Point p, double ang);
    // abstract methods may be accidently added later
}

避免使用功能性接口作为参数的重载方法

如果有两个或更多的具有相同名称的函数将功能性接口作为参数,那么这可能会在客户端侧导致lambda模糊。例如,如果有两个Point方法add(Function renderer) 和add(Predicate logCondition),并且我们尝试从客户端代码调用point.add(p -> p + “ lambda”) ,那么编译器会无法确定使用哪个方法,并产生错误。相反,请根据具体用途考虑命名方法。

你可以这样写

public interface Point {
    addRenderer(Function renderer);
    addLogCondition(Predicate logCondition);
}


而不要这样写

立即学习Java免费学习笔记(深入)”;

public interface Point {
    add(Function renderer);
    add(Predicate logCondition);
}

避免在接口中过度使用默认方法

默认方法可以很容易地添加到接口,有时这是有意义的。例如,想要一个对于任何实现类都期望是相同的并且在功能上要又短又“基本”的方法,那么一个可行的候选项就是默认实现。此外,当扩展API时,出于向后兼容性的原因,提供默认接口方法有时是有意义的。

众所周知,功能性接口只包含一个抽象方法,因此当必须添加其他方法时,默认方法提供了一个安全舱口。然而,通过用不必要的实现问题来污染API接口以避免API接口演变为实现类。如果有疑问,请考虑将方法逻辑移动到单独的实用程序类和/或将其放置在实现类中。

你可以这样写

public interface Line {
    Point start();
    Point end();
    int length();
}


而不要这样写

立即学习Java免费学习笔记(深入)”;

public interface Line {
    Point start();
    Point end();
    default int length() {
        int deltaX = start().x() - end().x();
        int deltaY = start().y() - end().y();
    return (int) Math.sqrt(
        deltaX * deltaX + deltaY * deltaY
        );
    }
}

确保在执行之前进行API方法的参数不变量检查

在历史上,人们一直草率地在确保验证方法输入参数。因此,当稍后发生结果错误时,真正的原因变得模糊并隐藏在堆栈跟踪下。确保在实现类中使用参数之前检查参数的空值和任何有效的范围约束或前提条件。不要因性能原因而跳过参数检查的诱惑。

JVM能够优化掉冗余检查并产生高效的代码。好好利用Objects.requireNonNull()方法。参数检查也是实施API约定的一个重要方法。如果不想API接受null但是却做了,用户会感到困惑。

你可以这样写

public void addToSegment(Segment segment, Point point) {
    Objects.requireNonNull(segment);
    Objects.requireNonNull(point);
    segment.add(point);
}


而不要这样写

立即学习Java免费学习笔记(深入)”;

public void addToSegment(Segment segment, Point point) {
    segment.add(point);
}

不要简单地调用Optional.get()

Java 8的API设计师犯了一个错误,在他们选择名称Optional.get()的时候,其实应该被命名为Optional.getOrThrow()或类似的东西。调用get()而没有检查一个值是否与Optional.isPresent()方法同在是一个非常常见的错误,这个错误完全否定了Optional原本承诺的null消除功能。考虑在API的实现类中使用任一Optional的其他方法,如map(),flatMap()或ifPresent(),或者确保在调用get()之前调用isPresent()。

你可以这样写

Optional comment = // some Optional value 
String guiText = comment
  .map(c -> "Comment: " + c)
  .orElse("");


而不要这样写

立即学习Java免费学习笔记(深入)”;

Optional comment = // some Optional value 
String guiText = "Comment: " + comment.get();

考虑在不同的API实现类中分行调用接口

最后,所有API都将包含错误。当接收来自于API用户的堆栈跟踪时,如果将不同的接口分割为不同的行,相比于在单行上表达更为简洁,而且确定错误的实际原因通常更容易。此外,代码可读性将提高。

你可以这样写

Stream.of("this", "is", "secret") 
  .map(toGreek()) 
  .map(encrypt()) 
  .collect(joining(" "));


而不要这样写

立即学习Java免费学习笔记(深入)”;

Stream.of("this", "is", "secret").map(toGreek()).map(encrypt()).collect(joining(" "));

           

 以上就是Java 8 API 设计经验浅析 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
全国统一发票查询平台入口合集
全国统一发票查询平台入口合集

本专题整合了全国统一发票查询入口地址合集,阅读专题下面的文章了解更多详细入口。

36

2026.02.03

短剧入口地址汇总
短剧入口地址汇总

本专题整合了短剧app推荐平台,阅读专题下面的文章了解更多详细入口。

102

2026.02.03

植物大战僵尸版本入口地址汇总
植物大战僵尸版本入口地址汇总

本专题整合了植物大战僵尸版本入口地址汇总,前往文章中寻找想要的答案。

49

2026.02.03

c语言中/相关合集
c语言中/相关合集

本专题整合了c语言中/的用法、含义解释。阅读专题下面的文章了解更多详细内容。

9

2026.02.03

漫蛙漫画网页版入口与正版在线阅读 漫蛙MANWA官网访问专题
漫蛙漫画网页版入口与正版在线阅读 漫蛙MANWA官网访问专题

本专题围绕漫蛙漫画(Manwa / Manwa2)官网网页版入口进行整理,涵盖漫蛙漫画官方主页访问方式、网页版在线阅读入口、台版正版漫画浏览说明及基础使用指引,帮助用户快速进入漫蛙漫画官网,稳定在线阅读正版漫画内容,避免误入非官方页面。

76

2026.02.03

Yandex官网入口与俄罗斯搜索引擎访问指南 Yandex中文登录与网页版入口
Yandex官网入口与俄罗斯搜索引擎访问指南 Yandex中文登录与网页版入口

本专题汇总了俄罗斯知名搜索引擎 Yandex 的官网入口、免登录访问地址、中文登录方法与网页版使用指南,帮助用户稳定访问 Yandex 官网,并提供一站式入口汇总。无论是登录入口还是在线搜索,用户都能快速获取最新稳定的访问链接与使用指南。

417

2026.02.03

Java 设计模式与重构实践
Java 设计模式与重构实践

本专题专注讲解 Java 中常用的设计模式,包括单例模式、工厂模式、观察者模式、策略模式等,并结合代码重构实践,帮助学习者掌握 如何运用设计模式优化代码结构,提高代码的可读性、可维护性和扩展性。通过具体示例,展示设计模式如何解决实际开发中的复杂问题。

4

2026.02.03

C# 并发与异步编程
C# 并发与异步编程

本专题系统讲解 C# 异步编程与并发控制,重点介绍 async 和 await 关键字、Task 类、线程池管理、并发数据结构、死锁与线程安全问题。通过多个实战项目,帮助学习者掌握 如何在 C# 中编写高效的异步代码,提升应用的并发性能与响应速度。

5

2026.02.03

Python 强化学习与深度Q网络(DQN)
Python 强化学习与深度Q网络(DQN)

本专题深入讲解 Python 在强化学习(Reinforcement Learning)中的应用,重点介绍 深度Q网络(DQN) 及其实现方法,涵盖 Q-learning 算法、深度学习与神经网络的结合、环境模拟与奖励机制设计、探索与利用的平衡等。通过构建一个简单的游戏AI,帮助学习者掌握 如何使用 Python 训练智能体在动态环境中作出决策。

4

2026.02.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
极客学院Java8新特性视频教程
极客学院Java8新特性视频教程

共17课时 | 3.8万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

微信小程序开发之API篇
微信小程序开发之API篇

共15课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号