How to define a member variable of boolean type in java

In daily development, we often need to define Boolean variables in the class. For example, when providing an RPC interface to an external system, we generally define a field to indicate whether the request is successful or not.

Regarding the definition of the field of "whether this request is successful", there are actually many kinds of particularities and pitfalls. If you are not careful, you will fall into the pit. The author has encountered similar problems a long time ago, so this article will come Let's do a simple analysis around this. In the end how to set a Boolean member variable.

In general, we can define a Boolean member variable in the following four ways:

boolean success
boolean isSuccess
Boolean success
Boolean isSuccess

Which of the above four definition forms is most commonly used in your daily development? Which one is the correct posture to use?

Through observation, we can find that the main difference between the first two and the latter two is the type of variable, the former uses boolean, and the latter uses boolean.

In addition, when the first and third types define variables, the variable name is success, while the other two use isSuccess to name.

First of all, let's analyze whether it should be named after success, or isSuccess is better.

success or isSuccess

Should the variable be named success or isSuccess? Semantically speaking, both naming methods are reasonable, and there is no ambiguity. So what other principles can we refer to to let us make choices?

Regarding this point in the Alibaba Java Development Manual, there is a "mandatory" regulation:

![-w656][1]

So, why is there such a regulation? Let's take a look at the difference between the different naming of Boolean variables in 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;
    }
}

The setter/getter of the above code is automatically generated using Intellij IDEA. If you observe the above code carefully, you will find the following rules:

  • The getter and setter methods automatically generated by the basic type have the same name isXXX()and setXXX()form.
  • The getter and setter methods automatically generated by the wrapping type have names getXXX()of setXXX()the form.

Now that we have reached a consensus to use the basic type boolean to define member variables, let's take a closer look at the difference between the setter/getter in Model3 and Model4.

We can find that although the names of the member variables in Model3 and Model4 are different, one is success and the other is isSuccess, but their automatically generated getter and setter method names are both isSuccessand setSuccess.

Specifications about setter/getter in Java Bean

The definition of the getter/setter method in Java Bean is actually clearly defined. According to [JavaBeans™ Specification][2], if it is an ordinary parameter propertyName, its setter/getter should be defined in the following way:

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

However, the Boolean variable propertyName is defined separately:

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

![-w687][3]

By comparing this JavaBeans specification, we found that in Model4, the variable name is isSuccess, if defined strictly according to the specification, its getter method should be called isIsSuccess. But many IDEs will generate isSuccess by default.

So what's the problem with doing so?

In general, there is actually no effect. But there is a special case where there is a problem, and that is when serialization occurs.

The impact of serialization

For serialization and deserialization, please refer to [Serialization and deserialization of Java objects][4]. Let's take the more commonly used JSON serialization as an example to see the difference between the commonly used fastJson, jackson and 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";
    }
}

In Model3 of the above code, there is only one member variable, isSuccess, and three methods, namely isSuccess and setSuccess automatically generated by the IDE for us, and the other is a method added by the author himself that conforms to the getter naming convention.

The output of the above code is:

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

In the results of fastjson and jackson, the isSuccess field in the original class is serialized into success, and it also contains the hollis value. And Gson only has the isSuccess field.

We can draw a conclusion: when fastjson and jackson serialize objects into json strings, they traverse all the getter methods in the class through reflection to get getHollis and isSuccess, and then according to JavaBeans rules, he will think that these are two The value of the attributes hollis and success. Direct serialization into json: {"hollis": "hollishuang", "success": true}

But Gson does not do this. He traverses all properties in the class through reflection and serializes their values ​​into json: {"isSuccess": true}

It can be seen that due to different serialization tools, the strategies used in serialization are different, so the serialization results for the same object of the same class may be different.

The serialization of getHollis mentioned above is just to illustrate the difference in serialization strategies between fastjson, jackson and Gson. Let’s put him aside for now. After we delete him from Model3, re-execute the above code. got the answer:

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

Now, the json content obtained by different serialization frameworks is not the same. If I use fastjson to serialize the same object, and then use Gson to deserialize it, what will happen?

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;
    }
    @Override
    public String toString() {
        return new StringJoiner(", ", Model3.class.getSimpleName() + "[", "]")
            .add("isSuccess=" + isSuccess)
            .toString();
    }
}

The above code, the output result:

Model3[isSuccess=false]

This is completely contrary to our expected result. The reason is that the JSON framework finds an isSuccess method after scanning all the getters, and then parses out the variable name success according to the JavaBeans specification, serializes the model object into a string, and the content is {"success":true}.

According to {"success":true}this json string, the Gson framework searches for the success attribute in the Model class through reflection after parsing, but there is only the isSuccess attribute in the Model class. Therefore, in the final deserialized Model class object, isSuccess will use the default value false.

However, once the above code occurs in the production environment, this is definitely a fatal problem.

Therefore, as developers, we should find ways to avoid this kind of problem as much as possible. For POJO designers, they only need to do one simple thing to solve this problem, that is, change isSuccess to success. In this way, the member variable in this class is success, and the getter method is isSuccess, which fully complies with the JavaBeans specification. Regardless of the serialization framework, the execution results are the same. Just avoid this problem from the source.

Quoting the following comments from the University of R on the Alibaba Java Development Manual (https://www.zhihu.com/question/55642203):

![-w665][5]

Therefore, when defining Boolean variables in POJO, do not use the form of isSuccess, but use success directly!

Boolean is boolean

We have introduced how to choose between success and isSuccess earlier, so after eliminating the wrong answer, the remaining options are:

boolean success
Boolean success

So, should Boolean or boolean be used to give a variable of Boolean type?

We know that boolean is a basic data type, and Boolean is a packaging type. For the relationship and difference between basic data types and packaging classes, please refer to [One article to understand what is automatic unboxing in Java][6]

So, is it better to use a wrapper type or a basic data type when defining a member variable?

Let's look at a simple piece of code

 /**
 * @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
     */
    @Override
    public String toString() {
        return new StringJoiner(", ", Model.class.getSimpleName() + "[", "]")
            .add("success=" + success)
            .add("failure=" + failure)
            .toString();
    }
}

The output of the above code is:

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

It can be seen that when we do not set the value of the field of the Model object, the variable of type Boolean will set the default value null, and the variable of type boolean will set the default value false.

That is, the default value of the object is null, and the default value of the boolean basic data type is false.

In the Alibaba Java Development Manual, there are also some regulations on how to select the type of variables in POJO:

It is suggested that we use the packaging type here. What is the reason?

To give an example of fee deduction, we are making a fee deduction system. When deducting fees, we need to read a rate value from an external pricing system. We expect that the return value of this interface will contain a floating-point rate field. When we get this value, we use the formula: amount * rate = fee to calculate, and the calculation result is deducted.

If the billing system is abnormal, it may return a default value. If the field is of Double type, the default value is null, and if the field is of double type, the default value is 0.0.

If the deduction system does not do special processing for the return value of the rate, it will directly report an error and block the program if it gets a null value for calculation. If you get 0.0, you may directly calculate it, and after the interface is 0, you will deduct the fee. This abnormality cannot be perceived.

This way of defining variables using wrapper types blocks the program through exceptions, and then can be identified to this kind of online problem. If the basic data type is used, the system may not report an error, and then consider that there is no exception.

The above is why it is recommended to use wrapper types in POJO and RPC return values.

But on this point, the author has had a different opinion before: for variables of Boolean type, I think it can be distinguished from other types, and the author does not think that using null to cause NPE is a best practice. Because the boolean type has only two values ​​of true/false, we can agree with the external caller on the clear semantics when the return value is false.

Later, the author had a 1V1(qing) Battle(jiao) with the author of "Alibaba Java Development Manual" and "Code Out Efficiently" alone. In the end, a consensus was reached to use the packaging type as much as possible .

However, the author still wants to emphasize one of my points, try to avoid indeterminate null values ​​in your code.

Summarize

This article introduces the type and naming of Boolean variable definitions. Finally, we can conclude that when defining a Boolean variable, especially when it returns a value for an externally provided interface, it should be named after success. Alibaba The Java Development Manual recommends using wrapper classes to define variables in POJOs and RPC return values. But this does not mean that null can be used at will, we still have to try to avoid the processing of null.

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132307161