python 什么可以作为变量名_为什么强烈禁止开发人员使用isSuccess作为变量名

在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。

关于这个”本次请求是否成功”的字段的定义,其实是有很多种讲究和坑的,稍有不慎就会掉入坑里,作者在很久之前就遇到过类似的问题,本文就来围绕这个简单分析一下。到底该如何定一个布尔类型的成员变量。

一般情况下,我们可以有以下四种方式来定义一个布尔类型的成员变量:

boolean success
boolean isSuccess
Boolean success
Boolean isSuccess

以上四种定义形式,你日常开发中最常用的是哪种呢?到底哪一种才是正确的使用姿势呢?

通过观察我们可以发现,前两种和后两种的主要区别是变量的类型不同,前者使用的是boolean,后者使用的是Boolean。

另外,第一种和第三种在定义变量的时候,变量命名是success,而另外两种使用isSuccess来命名的。

首先,我们来分析一下,到底应该是用success来命名,还是使用isSuccess更好一点。

success 还是 isSuccess

到底应该是用success还是isSuccess来给变量命名呢?从语义上面来讲,两种命名方式都可以讲的通,并且也都没有歧义。那么还有什么原则可以参考来让我们做选择呢。

在阿里巴巴Java开发手册中关于这一点,有过一个『强制性』规定:

e4d970dc0af45ed6f3b7730d0892f49d.png

那么,为什么会有这样的规定呢?我们看一下POJO中布尔类型变量不同的命名有什么区别吧。

class Model1  {private Boolean isSuccess;public void setSuccess(Boolean success) {isSuccess = success;}public Boolean getSuccess() {return isSuccess;}}class Model2 {private Boolean success;public Boolean getSuccess() {return success;}public void setSuccess(Boolean success) {this.success = success;}
}class Model3 {private boolean isSuccess;public boolean isSuccess() {return isSuccess;}public void setSuccess(boolean success) {isSuccess = success;}
}class Model4 {private boolean success;public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}
}

以上代码的setter/getter是使用Intellij IDEA自动生成的,仔细观察以上代码,你会发现以下规律:

  • 基本类型自动生成的getter和setter方法,名称都是isXXX()setXXX()形式的。
  • 包装类型自动生成的getter和setter方法,名称都是getXXX()setXXX()形式的。

既然,我们已经达成一致共识使用基本类型boolean来定义成员变量了,那么我们再来具体看下Model3和Model4中的setter/getter有何区别。

我们可以发现,虽然Model3和Model4中的成员变量的名称不同,一个是success,另外一个是isSuccess,但是他们自动生成的getter和setter方法名称都是isSuccesssetSuccess

Java Bean中关于setter/getter的规范

关于Java Bean中的getter/setter方法的定义其实是有明确的规定的,根据JavaBeans(TM) Specification规定,如果是普通的参数propertyName,要以以下方式定义其setter/getter:

public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);

但是,布尔类型的变量propertyName则是单独定义的:

public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);

b9e14f8912abbc352cb3ed78f6ceb9a5.png

通过对照这份JavaBeans规范,我们发现,在Model4中,变量名为isSuccess,如果严格按照规范定义的话,他的getter方法应该叫isIsSuccess。但是很多IDE都会默认生成为isSuccess。

那这样做会带来什么问题呢。

在一般情况下,其实是没有影响的。但是有一种特殊情况就会有问题,那就是发生序列化的时候。

序列化带来的影响

关于序列化和反序列化请参考Java对象的序列化与反序列化。我们这里拿比较常用的JSON序列化来举例,看看看常用的fastJson、jackson和Gson之间有何区别:

public class BooleanMainTest {public static void main(String[] args) throws IOException {//定一个Model3类型Model3 model3 = new Model3();model3.setSuccess(true);//使用fastjson(1.2.16)序列化model3成字符串并输出System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model3));//使用Gson(2.8.5)序列化model3成字符串并输出Gson gson =new Gson();System.out.println("Serializable Result With Gson :" +gson.toJson(model3));//使用jackson(2.9.7)序列化model3成字符串并输出ObjectMapper om = new ObjectMapper();System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model3));}}class Model3 implements Serializable {private static final long serialVersionUID = 1836697963736227954L;private boolean isSuccess;public boolean isSuccess() {return isSuccess;}public void setSuccess(boolean success) {isSuccess = success;}public String getHollis(){return "hollischuang";}
}

以上代码的Model3中,只有一个成员变量即isSuccess,三个方法,分别是IDE帮我们自动生成的isSuccess和setSuccess,另外一个是作者自己增加的一个符合getter命名规范的方法。

以上代码输出结果:

Serializable Result With fastjson :{"hollis":"hollischuang","success":true}
Serializable Result With Gson :{"isSuccess":true}
Serializable Result With jackson :{"success":true,"hollis":"hollischuang"}

在fastjson和jackson的结果中,原来类中的isSuccess字段被序列化成success,并且其中还包含hollis值。而Gson中只有isSuccess字段。

我们可以得出结论:fastjson和jackson在把对象序列化成json字符串的时候,是通过反射遍历出该类中的所有getter方法,得到getHollis和isSuccess,然后根据JavaBeans规则,他会认为这是两个属性hollis和success的值。直接序列化成json:{“hollis”:”hollischuang”,”success”:true}

但是Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json:{“isSuccess”:true}

可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,所以,对于同一个类的同一个对象的序列化结果可能是不同的。

前面提到的关于对getHollis的序列化只是为了说明fastjson、jackson和Gson之间的序列化策略的不同,我们暂且把他放到一边,我们把他从Model3中删除后,重新执行下以上代码,得到结果:

Serializable Result With fastjson :{"success":true}
Serializable Result With Gson :{"isSuccess":true}
Serializable Result With jackson :{"success":true}

现在,不同的序列化框架得到的json内容并不相同,如果对于同一个对象,我使用fastjson进行序列化,再使用Gson反序列化会发生什么?

public class BooleanMainTest {public static void main(String[] args) throws IOException {Model3 model3 = new Model3();model3.setSuccess(true);Gson gson =new Gson();System.out.println(gson.fromJson(JSON.toJSONString(model3),Model3.class));}
}class Model3 implements Serializable {private static final long serialVersionUID = 1836697963736227954L;private boolean isSuccess;public boolean isSuccess() {return isSuccess;}public void setSuccess(boolean success) {isSuccess = success;}@Overridepublic String toString() {return new StringJoiner(", ", Model3.class.getSimpleName() + "[", "]").add("isSuccess=" + isSuccess).toString();}
}

以上代码,输出结果:

Model3[isSuccess=false]

这和我们预期的结果完全相反,原因是因为JSON框架通过扫描所有的getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,把model对象序列化城字符串后内容为{"success":true}

根据{"success":true}这个json串,Gson框架在通过解析后,通过反射寻找Model类中的success属性,但是Model类中只有isSuccess属性,所以,最终反序列化后的Model类的对象中,isSuccess则会使用默认值false。

但是,一旦以上代码发生在生产环境,这绝对是一个致命的问题。

所以,作为开发者,我们应该想办法尽量避免这种问题的发生,对于POJO的设计者来说,只需要做简单的一件事就可以解决这个问题了,那就是把isSuccess改为success。这样,该类里面的成员变量时success,getter方法是isSuccess,这是完全符合JavaBeans规范的。无论哪种序列化框架,执行结果都一样。就从源头避免了这个问题。

引用以下R大关于阿里巴巴Java开发手册这条规定的评价(https://www.zhihu.com/question/55642203):

054b1407525196fa71357d66a3d302de.png

所以,在定义POJO中的布尔类型的变量时,不要使用isSuccess这种形式,而要直接使用success!

Boolean还是boolean?

前面我们介绍完了在success和isSuccess之间如何选择,那么排除错误答案后,备选项还剩下:

boolean success
Boolean success

那么,到底应该是用Boolean还是boolean来给定一个布尔类型的变量呢?

我们知道,boolean是基本数据类型,而Boolean是包装类型。关于基本数据类型和包装类之间的关系和区别请参考一文读懂什么是Java中的自动拆装箱

那么,在定义一个成员变量的时候到底是使用包装类型更好还是使用基本数据类型呢?

我们来看一段简单的代码

/*** @author Hollis*/
public class BooleanMainTest {public static void main(String[] args) {Model model1 = new Model();System.out.println("default model : " + model1);}
}class Model {/*** 定一个Boolean类型的success成员变量*/private Boolean success;/*** 定一个boolean类型的failure成员变量*/private boolean failure;/*** 覆盖toString方法,使用Java 8 的StringJoiner*/@Overridepublic String toString() {return new StringJoiner(", ", Model.class.getSimpleName() + "[", "]").add("success=" + success).add("failure=" + failure).toString();}
}

以上代码输出结果为:

default model : Model[success=null, failure=false]

可以看到,当我们没有设置Model对象的字段的值的时候,Boolean类型的变量会设置默认值为null,而boolean类型的变量会设置默认值为false

即对象的默认值是null,boolean基本数据类型的默认值是false

在阿里巴巴Java开发手册中,对于POJO中如何选择变量的类型也有着一些规定:

863319be147daebf0b85224a138c3848.png

这里建议我们使用包装类型,原因是什么呢?

举一个扣费的例子,我们做一个扣费系统,扣费时需要从外部的定价系统中读取一个费率的值,我们预期该接口的返回值中会包含一个浮点型的费率字段。当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。

如果由于计费系统异常,他可能会返回个默认值,如果这个字段是Double类型的话,该默认值为null,如果该字段是double类型的话,该默认值为0.0。

如果扣费系统对于该费率返回值没做特殊处理的话,拿到null值进行计算会直接报错,阻断程序。拿到0.0可能就直接进行计算,得出接口为0后进行扣费了。这种异常情况就无法被感知。

这种使用包装类型定义变量的方式,通过异常来阻断程序,进而可以被识别到这种线上问题。如果使用基本数据类型的话,系统可能不会报错,进而认为无异常。

以上,就是建议在POJO和RPC的返回值中使用包装类型的原因。

但是关于这一点,作者之前也有过不同的看法:对于布尔类型的变量,我认为可以和其他类型区分开来,作者并不认为使用null进而导致NPE是一种最好的实践。因为布尔类型只有true/false两种值,我们完全可以和外部调用方约定好当返回值为false时的明确语义。

后来,作者单独和《阿里巴巴Java开发手册》、《码出高效》的作者——孤尽 单独1V1(qing) Battle(jiao)了一下。最终达成共识,还是尽量使用包装类型

但是,作者还是想强调一个我的观点,尽量避免在你的代码中出现不确定的null值。

null何罪之有?

关于null值的使用,我在使用Optional避免NullPointerException9 Things about Null in Java等文中就介绍过。

null是很模棱两可的,很多时候会导致令人疑惑的的错误,很难去判断返回一个null代表着什么意思。

图灵奖得主Tony Hoare 曾经公开表达过null是一个糟糕的设计。

c8c7e2696580ed1c84371a7467980ca0.png

我把 null 引用称为自己的十亿美元错误。它的发明是在1965 年,那时我用一个面向对象语言( ALGOL W )设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了Null引用,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。

当我们在设计一个接口的时候,对于接口的返回值的定义,尽量避免使用Boolean类型来定义。大多数情况下,别人使用我们的接口返回值时可能用if(response.isSuccess){}else{}的方式,如果我们由于忽略没有设置success字段的值,就可能导致NPE(java.lang.NullPointerException),这明显是我们不希望看到的。

所以,当我们要定义一个布尔类型的成员变量时,尽量选择boolean,而不是Boolean。当然,编程中并没有绝对。

总结

本文围绕布尔类型的变量定义的类型和命名展开了介绍,最终我们可以得出结论,在定义一个布尔类型的变量,尤其是一个给外部提供的接口返回值时,要使用success来命名,阿里巴巴Java开发手册建议使用封装类来定义POJO和RPC返回值中的变量。但是这不意味着可以随意的使用null,我们还是要尽量避免出现对null的处理的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自建Git服务器系列——Gitea(Gogs的孪生兄弟)

概述该项目的目标是提供一种最简单&#xff0c;最快&#xff0c;最轻松的方式来建立自托管的Git服务。使用Go&#xff0c;可以在Go支持的所有平台上进行独立的二进制分发 &#xff0c;包括x86&#xff0c;amd64&#xff0c;ARM和PowerPC体系结构上的Linux&#xff0c;macOS和Wi…

干货|吴恩达Coursera课程教你学习神经网络!

吴恩达Coursera机器学习课程系列笔记讲解课程笔记|吴恩达Coursera机器学习 Week1 笔记-机器学习基础干货|机器学习零基础&#xff1f;不要怕&#xff0c;吴恩达机器学习课程笔记2-多元线性回归干货|机器学习零基础&#xff1f;不要怕&#xff0c;吴恩达课程笔记第三周&#xff…

笔记本内置扬声器三强PK

内置扬声器PK要点外观设计差异。外表是否美观直接影响使用者的心情&#xff0c;扬声器的结构设计直接影响放音效果。实际听音较量。利用真实的人耳感受则是最能体现扬声器实际效果的。奥特蓝星&#xff1a;音质纯净&#xff0c;低音欠佳代表机型&#xff1a;惠普&#xff0c;华…

mysql内连接查询原理_MySQL全面瓦解12:连接查询的原理和应用

概述MySQL最强大的功能之一就是能在数据检索的执行中连接(join)表。大部分的单表数据查询并不能满足我们的需求&#xff0c;这时候我们就需要连接一个或者多个表&#xff0c;并通过一些条件过滤筛选出我们需要的数据。了解MySQL连接查询之前我们先来理解下笛卡尔积的原理。数据…

如何在 .NET 中使用 Kafka

Kafka 是一个开源的&#xff0c;分布式的&#xff0c;可扩展的&#xff0c;高性能的发布订阅模式的消息中间件&#xff0c;如果你要构建一个处理海量数据的系统&#xff0c;那么 Kafka 将会是一个非常好的选择&#xff0c;这篇文章我们将会讨论如何基于 Kakfa 构建一个发布订阅…

傅里叶变换和拉普拉斯变换的物理解释及区别

傅里叶变换在物理学、数论、组合数学、信号处理、概率论、统计学、密码学、声学、光学、海洋学、结构动力学等领域都有着广泛的应用&#xff08;例如在信号处理中&#xff0c;傅里叶变换的典型用途是将信号分解成幅值分量和频率分量&#xff09;。傅里叶变换能将满足一定条件的…

Teleport 开源堡垒机的使用

公司的服务器可能会存在这样一种情况&#xff0c;具体的应用是部署在一个或多个内网服务器上&#xff0c;然后由一台外网服务器通过代理的方式对外提供服务&#xff0c;例如下图&#xff1a;我们如果需要进入到内网服务器进行操作就必须先要进入外网服务器&#xff0c;然后再远…

python拼图游戏_乐趣无穷的Python课堂

Python world/特慧编/你所认为的.........pythonpython&枯燥、无趣boring“安全”提示走进特慧编走进“python编程课”让我们进入真正的编程世界&#xff0c;培养逻辑数理思维&#xff0c;学习掌握python特色&#xff0c;让你的学习过程不再枯燥、不再无趣~~~下面跟着我的脚…

让 Python 更加充分的使用 Sqlite3

我最近在涉及大量数据处理的项目中频繁使用 sqlite3。我最初的尝试根本不涉及任何数据库&#xff0c;所有的数据都将保存在内存中&#xff0c;包括字典查找、迭代和条件等查询。这很好&#xff0c;但可以放入内存的只有那么多&#xff0c;并且将数据从磁盘重新生成或加载到内存…

techempower之Plaintext上7百万RPS

在Plaintext这项测试中第一阶梯的分隔线基本算是7百万RPS&#xff0c;Beetlex并没有到到这一阶梯停留在69X万RPS处&#xff0c;虽然只差那数万但在排名上让人感觉不爽。Beetlex在很多项测都微微领先aspcore,但在最基础项落下一点点的确让我感觉到不太满意&#xff0c;更希望Bee…

详解全排列算法

简介给定 {1, 2, 3, , , n}&#xff0c;其全排列为 n! 个&#xff0c;这是最基础的高中组合数学知识。我们以 n4 为例&#xff0c;其全部排列如下图&#xff08;以字典序树形式来呈现&#xff09;&#xff1a;我们很容易想到用递归来求出它的所有全排列。仔细观察上图&#xff…

VS2019 调试技巧之附加进程

C# 创建服务并附加到进程进行调试步骤一&#xff1a;在任务栏右键-》》点击任务管理器-》》选择服务&#xff0c;找到启动的进程PID或者WINR 进入cmd命令 输入 netstat -ano | find "进程端口" 找端口步骤二&#xff1a;VS中找到“调试”菜单&#xff0c;选择“…

sql同时向两个表插入数据_SQL入门-数据库和客户端的安装,表的创建和数据插入...

1、如何验证MySQL数据库安装成功按照上图操作打开SQL命令行客户端输入安装MySQL时设置的密码并按enter键&#xff0c;得到下图&#xff1a;如果有显示出来红框里的内容&#xff0c;就表示安装成功。红框里的内容表示的是MySQL数据库版本号。2、如何用客户端&#xff08;Navicat…

我是怎么进入Oracle这样的大企业的?

导语&#xff1a;人工智能是泡沫么&#xff1f;AI产业的未来将何去何从&#xff1f;机器学习又该怎么学习&#xff1f;AI行业从业者又是怎么看待这个行业的呢&#xff1f;踏入一个行业之前最好对这个行业有个全方位的了解。本文作者饶毅&#xff0c;现就职于甲骨文公司。AI行业…

websocket文档_WebSocket推送 原理扫盲到上手实践

关于服务端推送技术&#xff0c;大家比较熟悉的可能就是轮询&#xff0c;但是轮询只能是由客户端先发起http请求。在HTTP1.1中的keep-alive方式建立的http连接&#xff0c;但是一个Request只能对应一个Response&#xff0c;而且这个Response是被动的&#xff0c;不能主动发起。…

DISCUZ7.2在通达OA2009桌面显示技巧

最近在测试DISCUZ 和通达...猛然间看到,,,可以DISCUZ可以和通达完美结合,禁不住进行了测试.....效果还挺好的...最初效果图如下:感觉挺别扭的,于是将DISCUZ调用代码更改了代码如下:[show1] <table width"100%" > <tr> <td alignleft> <di…

如何在 ASP.Net Core 中使用 Lamar

ASP.Net Core 自带了一个极简的 开箱即用 的依赖注入容器&#xff0c;实际上&#xff0c;你还可以使用第三方的 依赖注入容器 来替代它&#xff0c;依赖注入是一种设计模式&#xff0c;它能够有效的实现对象之间的解耦并有利于提高单元测试和维护性&#xff0c;你可以使用 依赖…

扎克伯格做了26张PPT,员工效率提10倍,已被疯狂传阅!

1、时间常有&#xff0c;时间在于优先。2、时间总会有的&#xff1a;每天只计划 4&#xff5e;5 小时真正的工作。3、当你在状态时&#xff0c;就多干点&#xff1b;不然就好好休息&#xff1a;有时候会连着几天不是工作状态&#xff0c;有时在工作状态时却又能天天忙活 12 小时…

2010南非世界杯32强手绘海报

2010南非世界杯32强手绘海报 2010年南非世界杯已经进入最后的倒计时&#xff0c;近日&#xff0c;ESPN推出了一组以世界杯32强为主题的手绘海报。在这组颇有漫画性质的海报中&#xff0c;32强每支球队的特点都是展现得淋淋尽致&#xff0c;卡卡、梅西、C罗、托雷斯等球星也自然…

鹅厂二面,Nginx回忆录

上周二面鹅厂&#xff0c;面试官问出了“nginx你了解吗&#xff1f;”这样宽泛直白的句式&#xff0c;我一时抓不到重点&#xff0c;一时语噻。下班想了一下&#xff0c;平时潜移默化用到不少nginx的能力&#xff0c;但在面试的时候没有吹成对应的概念。面谈nginx核心能力nginx…