日常の開発では、クラス内にブール変数を定義する必要があることがよくあります。たとえば、外部システムに RPC インターフェイスを提供する場合、通常、リクエストが成功したかどうかを示すフィールドを定義します。
「この依頼が成功するかどうか」という分野の定義ですが、実は色々な特殊性や落とし穴があり、気をつけないと落とし穴にはまってしまいます。筆者も昔同じような問題に遭遇したことがあるので、この記事は今後の予定です。これを中心に簡単な分析を行ってみましょう。最後に、ブール型メンバー変数を設定する方法について説明します。
一般に、ブール型メンバー変数は次の 4 つの方法で定義できます。
boolean success
boolean isSuccess
Boolean success
Boolean isSuccess
上記の 4 つの定義形式のうち、日常の開発で最も一般的に使用されるのはどれですか? 正しい姿勢はどれでしょうか?
観察を通じて、最初の 2 つと後の 2 つの主な違いは変数のタイプであり、前者はブール値を使用し、後者はブール値を使用することがわかります。
さらに、最初と 3 番目のタイプが変数を定義する場合、変数名は success ですが、他の 2 つは名前に isSuccess を使用します。
まず最初に、Success にちなんだ名前を付ける必要があるのか、それとも isSuccess の方が良いのかを分析してみましょう。
成功か成功か
変数の名前は success または isSuccess にする必要がありますか? 意味的に言えば、どちらの命名方法も合理的であり、曖昧さはありません。それでは、選択をするために他にどのような原則を参照できるでしょうか?
この点に関して、Alibaba Java 開発マニュアルには「必須」の規定があります。
![-w656][1]
では、なぜそのような規制があるのでしょうか?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;
}
}
上記のコードのセッター/ゲッターは Intellij IDEA を使用して自動的に生成されます。上記のコードを注意深く観察すると、次の規則があることがわかります。
- 基本型によって自動的に生成される getter メソッドと setter メソッドは、同じ名前
isXXX()
とsetXXX()
形式を持ちます。 - ラッピング型によって自動的に生成されるゲッター メソッドとセッター メソッドには、フォーム
getXXX()
の名前が付けられます。setXXX()
メンバー変数の定義に基本型ブール値を使用するという合意に達したので、Model3 と Model4 のセッター/ゲッターの違いを詳しく見てみましょう。
Model3 と Model4 のメンバー変数の名前は異なり、1 つは success、もう 1 つは isSuccess ですが、自動的に生成されるゲッター メソッド名とセッター メソッド名は両方とも と であることがわかりisSuccess
ますsetSuccess
。
Java Beanのsetter/getterに関する仕様
Java Bean における getter/setter メソッドの定義は実は明確に定義されており、[JavaBeans™ 仕様][2] によれば、通常のパラメータ propertyName の場合、その setter/getter は次のように定義される必要があります。
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);
ただし、ブール変数 propertyName は個別に定義されます。
public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);
![-w687][3]
この JavaBeans 仕様を比較すると、Model4 では変数名が isSuccess であり、仕様に従って厳密に定義されている場合、そのゲッター メソッドは isIsSuccess と呼ばれるはずであることがわかりました。ただし、多くの IDE はデフォルトで isSuccess を生成します。
では、そうすることで何が問題になるのでしょうか?
一般に、実際には効果はありません。ただし、問題が発生する特殊なケースがあり、その場合にシリアル化が発生します。
連載化の影響
シリアル化と逆シリアル化については、[Java オブジェクトのシリアル化と逆シリアル化][4]を参照してください。より一般的に使用される 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 が 1 つと、IDE によって自動的に生成された isSuccess と setSuccess の 3 つのメソッドだけがあり、もう 1 つはゲッターの命名規則に準拠して作成者自身が追加したメソッドです。 。
上記のコードの出力は次のとおりです。
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 文字列にシリアル化するとき、リフレクションを通じてクラス内のすべてのゲッター メソッドを走査して getHollis と isSuccess を取得します。その後、JavaBeans のルールに従って、これらが 2 つの値であると考えるでしょう。属性ホリスと成功。json への直接シリアル化: {"hollis": "hollishuang", "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;
}
@Override
public String toString() {
return new StringJoiner(", ", Model3.class.getSimpleName() + "[", "]")
.add("isSuccess=" + isSuccess)
.toString();
}
}
上記のコード、出力結果は次のとおりです。
Model3[isSuccess=false]
これは、予期した結果に完全に反しています。その理由は、JSON フレームワークがすべてのゲッターをスキャンした後に isSuccess メソッドを見つけ、JavaBeans 仕様に従って変数名 success を解析し、モデル オブジェクトを文字列にシリアル化し、内容は{"success":true}
。
この json 文字列に従って{"success":true}
、Gson フレームワークは解析後のリフレクションを通じて Model クラスの success 属性を検索しますが、Model クラスには isSuccess 属性しかないため、最終的に逆シリアル化された Model クラス オブジェクトでは、isSuccess はデフォルトの属性を使用します。値は false。
ただし、上記のコードが運用環境で発生すると、これは間違いなく致命的な問題になります。
したがって、開発者としては、この種の問題を可能な限り回避する方法を見つける必要があり、POJO 設計者がこの問題を解決するには、isSuccess を success に変更するという 1 つの簡単な操作を行うだけで済みます。このように、このクラスのメンバー変数は success であり、ゲッター メソッドは isSuccess であり、JavaBeans 仕様に完全に準拠しています。シリアル化フレームワークに関係なく、実行結果は同じです。この問題をソースから回避してください。
Alibaba Java 開発マニュアル (https://www.zhihu.com/question/55642203) に関する R 大学の次のコメントを引用します。
![-w665][5]
したがって、POJO でブール変数を定義するときは、isSuccess の形式を使用せず、success を直接使用してください。
Boolean还是boolean
success と isSuccess のどちらかを選択する方法を以前に紹介しました。そのため、間違った答えを排除した後、残りのオプションは次のとおりです。
boolean success
Boolean success
では、ブール型の変数を与えるには、Boolean または boolean を使用する必要がありますか?
ブール型は基本的なデータ型であり、ブール型はパッケージ化型であることがわかっています。基本的なデータ型とパッケージ化クラスの関係と違いについては、[Java の自動アンボックス化とは何かを理解するための 1 つの記事][6] を参照してください。
では、メンバー変数を定義する場合、ラッパー型と基本データ型のどちらを使用するのが良いでしょうか?
簡単なコードを見てみましょう
/**
* @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();
}
}
上記のコードの出力は次のとおりです。
default model : Model[success=null, failure=false]
null
Model オブジェクトのフィールドの値を設定しない場合、Boolean 型の変数がデフォルト値を設定し、boolean 型の変数がデフォルト値を設定することがわかりますfalse
。
つまり、オブジェクトのデフォルト値は でありnull
、ブール基本データ型のデフォルト値は ですfalse
。
Alibaba Java Development Manual には、POJO で変数のタイプを選択する方法に関するいくつかの規定もあります。
ここではパッケージタイプを使用することが推奨されていますが、その理由は何ですか?
手数料控除の例を挙げると、手数料控除システムを作成しています。手数料を控除するときは、外部の価格設定システムからレート値を読み取る必要があります。このインターフェイスの戻り値には浮動小数点レート フィールドが含まれることが期待されます。 。この値を取得したら、「金額 * 料金 = 手数料」という計算式を使用して計算し、計算結果が差し引かれます。
課金システムが異常な場合、デフォルト値が返される場合があります。フィールドが Double 型の場合、デフォルト値は null、フィールドが double 型の場合、デフォルト値は 0.0 です。
控除システムがレートの戻り値に対して特別な処理を行わない場合、計算のために NULL 値を取得すると、直接エラーが報告され、プログラムがブロックされます。0.0 が出た場合は直接計算して、インターフェースが 0 になった後は手数料を差し引きます。この異常は認識できません。
ラッパー型を使用して変数を定義するこの方法では、例外によってプログラムがブロックされ、この種のオンライン問題として特定されることがあります。基本データ型が使用されている場合、システムはエラーを報告せず、例外がないとみなします。
上記が、POJO および RPC の戻り値でラッパー型を使用することが推奨される理由です。
しかし、この点について、著者は以前に異なる意見を持っていました。ブール型の変数については、他の型と区別できると考えており、NPE を引き起こすために null を使用することがベスト プラクティスであるとは考えていません。ブール型には true/false の 2 つの値しかないため、戻り値が false の場合、明確なセマンティクスについて外部呼び出し元と合意できます。
その後、著者は『Alibaba Java Development Manual』と『Code Out Efficiently』の著者と単独で 1V1(qing) Battle(jiao) をしました。最終的には、可能な限りパッケージタイプを使用するという合意に達しました。
ただし、著者は依然として私のポイントの 1 つを強調したいと考えています。コード内で不定の null 値を避けるようにしてください。
要約する
この記事では、ブール変数定義のタイプと命名について紹介します。最後に、ブール変数を定義するとき、特に外部提供されたインターフェイスの値を返すときは、成功後に名前を付ける必要があると結論付けることができます。Alibaba Java 開発マニュアルでは、次の使用を推奨しています。 POJO の変数と RPC 戻り値を定義するラッパー クラス。ただし、これは null を自由に使用できるという意味ではなく、null の処理を回避するように努める必要があります。