编程技巧:使用整数同时进行多个true|false判断
情景 :
假设需要判断某银行用户的其中一个账号(profileA),币种(Currency)为人民币(CNY),余额是否大于1,0000,然后进行某业务逻辑处理。
概述:
为了进行这种判断,需要判断/验证该账号是否为profileA、币种是否为CNY,余额是否大于1,0000这3种情况,在代码中可能会写出一大堆if...else语句,不仅不雅观,还不便于理解。如果使用一个整数int来同时标识多个判断的结果,就可以避免一大堆if...else的情况。当然这个例子只是作为demo说明情况,具体怎么处理还得根据实际出发,而且要是需要作更多判断,这个方法就会更实用。
分析说明:
对于计算机来说,所有数据都是由二进制数来表示,对于每一位(bit)来说,有0或1这2种情况,分别对应的boolean值为false和true。在Java中,每个int的大小为4 byte(注意是基本类型的int,而不是引用类型的Integer),一共有4*8=32位(bit),每一个位可对应一个boolean值的话,那么1个int最多可以同时对应32个boolean值。
以JDK中的实现,java.lang.reflect.Modifier为例:
- 定义每个二进制位数上的含义(modifier),并得到相应十六进制数;(其实十进制也可以,只是在十六进制下其实就是1/2/4/8这几个数字在不同的位上循环,比十进制数要直观,可以在下图感受一下)
- 如果同时验证多个条件,可以通过逻辑与(OR, "|")将多个条件组合起来。例如可以自定义“同时满足public、static、final这3个修饰词”这个条件:
- public static final int PUBLIC_STATIC_FINAL = PUBLIC | STATIC | FINAL;
- 将要验证的编码(整数)传入,与modifier进行逻辑与(AND, "&")运算,所得结果再与modifier比较即可;
- return (mod & modifier) == modifier;
1 public class Modifier{ 2 public static final int PUBLIC = 0x00000001; //0001 3 public static final int PRIVATE = 0x00000002; //0010 4 public static final int PROTECTED = 0x00000004; //0100 5 public static final int STATIC = 0x00000008; //1000 6 ...... 7 public static boolean isPublic(int mod) { 8 return (mod & PUBLIC) != 0; 9 } 10 ...... 11 }
验证:主要看System.out.println();打印输出的几行即可,其他都是辅助理解。
1 package com.scv.lawrence; 2 import java.lang.reflect.Field; 3 import java.lang.reflect.Modifier; 4 5 public class Demo { 6 public static final int STATIC_FINAL = Modifier.STATIC | Modifier.FINAL; 7 public static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; 8 9 public static boolean compare(int mod, int token){ 10 return (mod & token) == token; 11 } 12 13 private static int age = 999; 14 private static final String name = "Lawrence"; 15 public final String gender = "Male"; 16 17 public static final int PUBLIC_STATIC = Modifier.PUBLIC | Modifier.STATIC; 18 19 public static void main(String[] args) throws Exception { 20 Field myAge = Demo.class.getDeclaredField("age"); 21 Field myName = Demo.class.getDeclaredField("name"); 22 Field myGender = Demo.class.getDeclaredField("gender"); 23 24 int ageMod = myAge.getModifiers(); 25 int nameMod = myName.getModifiers(); 26 int genderMod = myGender.getModifiers(); 27 28 System.out.println("age的modifier(十进制): " + ageMod); 29 System.out.println("\t" + "是否包含static和final:" + compare(ageMod, STATIC_FINAL)); 30 System.out.println("\t" + "是否包含static:" + compare(ageMod, Modifier.STATIC)); 31 32 System.out.println("************"); 33 System.out.println("name的modifier(十进制): " + nameMod); 34 System.out.println("\t" + "是否包含static和final:" + compare(nameMod, STATIC_FINAL)); 35 System.out.println("\t" + "是否包含static:" + compare(nameMod, Modifier.STATIC)); 36 37 System.out.println("************"); 38 System.out.println("gender的modifier(十进制): " + genderMod); 39 System.out.println("\t" + "是否包含static和final:" + compare(genderMod, STATIC_FINAL)); 40 System.out.println("\t" + "是否包含static:" + compare(genderMod, Modifier.STATIC)); 41 } 42 43 } 44 45 /* output: 46 * 47 * age的modifier(十进制): 10 48 * 是否包含static和final:false 49 * 是否包含static:true 50 * ************ 51 * name的modifier(十进制): 26 52 * 是否包含static和final:true 53 * 是否包含static:true 54 * ************ 55 * gender的modifier(十进制): 17 56 * 是否包含static和final:false 57 * 是否包含static:false 58 */
场景应用:
- 自定义Profile类,在实例化的时候就计算并保存其类型;
- 在ToolBox类中定义各种标识的含义;
- 在main()中对profile进行验证并执行相应业务逻辑;
1 package com.scv.lawrence; 2 3 public class Fragment { 4 5 public static void main(String[] args) { 6 Profile[] profiles = {new Profile("ABC0001", "CNY", 12_0000), 7 new Profile("SRC0001", "CNY", 21_0000), //target profile 8 new Profile("ZZZ666GRE", "USD", 9000)}; 9 10 for(Profile p: profiles){ 11 if(ToolBox.isTargetProfile(p, ToolBox.PRO_A_CNY_GT)){ 12 //Do something. 13 System.out.println("Got target profile."); 14 }; 15 } 16 17 } 18 19 } 20 21 class ToolBox{ 22 public static final int PROFILE_A = 0x0000_0001; //0001 23 public static final int CNY = 0x0000_0002; //0010 24 public static final int GT_10K = 0x0000_0004; //0100 25 public static final int LT_10K = 0x0000_0008; //1000 26 27 public static final int PRO_A_CNY_GT = PROFILE_A | CNY | GT_10K; //0111,profileA、币种为人民币、余额大于1,0000 28 public static final int PRO_A_CNY_LT = PROFILE_A | CNY | LT_10K; //1111,profileA、币种为人民币、余额不足1,0000 29 30 protected static void setToken(Profile p){ 31 if("SRC0001".equals(p.getName())) p.token += 0x00000001; 32 if("CNY".equals(p.getCurrency())) p.token += 0x00000002; 33 if(p.getBalance() > 1_0000) p.token += 0x00000004; 34 } 35 36 public static boolean isTargetProfile(Profile p, int mod){ 37 return (p.getToken() & mod) == mod; 38 } 39 } 40 41 class Profile{ 42 private String name; 43 private String currency; 44 private double balance; 45 protected int token; 46 47 public int getToken(){ 48 return this.token; 49 } 50 51 public Profile() {} 52 public Profile(String name, String currency, double balance){ 53 this.name = name; 54 this.currency = currency; 55 this.balance = balance; 56 ToolBox.setToken(this); 57 } 58 public String getName() { 59 return name; 60 } 61 public void setName(String name) { 62 this.name = name; 63 } 64 public String getCurrency() { 65 return currency; 66 } 67 public void setCurrency(String currency) { 68 this.currency = currency; 69 } 70 public double getBalance() { 71 return balance; 72 } 73 public void setBalance(double balance) { 74 this.balance = balance; 75 }; 76 77 78 }
如要添加新的业务,只需要在ToolBox定义新的内容,即可。
p.s:数字下划线是JDK 1.7的语法糖,仅有方便阅读(人类)的作用,编译时会自动去掉。如1_0000_0000、100_000_000、100000000这几个数字在编译后都一样,但极大地方便了阅读。
优点:
- 代码量少,避免大量if...else判断,易于维护;
- 高效;
缺点:
- 初次接触的话可能不易理解;
与switch相比:
- 轻松进行多个条件的判断,且可以进行子集条件的判断,即对于满足A|B|C|D的条件,也会同时满足A|B、B|D或B|C|D等等。switch并不具备这样的能力。
如有纰漏,还望指正。