V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sandman511
V2EX  ›  程序员

Java :如何处理空指针?

  •  1
     
  •   sandman511 · 2019-12-31 11:16:04 +08:00 · 5579 次点击
    这是一个创建于 1568 天前的主题,其中的信息可能已经有所发展或是发生改变。
    Integer code = getCode();
    String codeMeaning = code == null ? null :
                        code == 1 ? "你好" :
                        code == 2 ? "你好好" :
                        code == 3 ? "你好好好";
    

    请各大佬帮忙修改这段代码
    code == null ? null 看起来有点怪怪的

    第 1 条附言  ·  2019-12-31 12:22:39 +08:00

    看了大家的建议 收获颇丰
    我比较喜欢简短一点的代码(在不影响可读性的情况下) 所以比较喜欢三元。。大家轻喷
    因为业务原因 code=1d的情况下 codeMeaning不一定是”你好“,所以不能使用枚举类或者Map
    目前我采用了9楼的写法 代码美观且解决了空指针的问题(楼上几位的代码也很好)
    但这样写代码从4行增加到了9行,有没有更简洁且美观的写法。
    大家轻喷哈,刚毕业。。啥都不懂

    第 2 条附言  ·  2019-12-31 14:28:51 +08:00

    为了方便大家理解真正的需求,更新下简化版的实际代码:

    //方法1
    Integer unit = getUnit (type1);
    String transferredUnit = unit == null ? null :
                        unit == 1 ? "kg" :
                        unit == 2 ? "g" :
                        unit == 3 ? "t";
    
    //方法2
    Integer unit = getUnit (type2);
    String transferredUnit = unit == null ? null :
                        unit == -1 ? "kg" :
                        unit == 0 ? "g" :
                        unit == 1 ? "t";
    
    //方法3
    Integer unit = getUnit (type3);
    String transferredUnit = unit == null ? null :
                        unit == 4 ? "kg" :
                        unit == 5 ? "g" :
                        unit == 6 ? "t";
    

    这样的情况用枚举类是化简为烦了吧应该,因为要针对不同type创建多个枚举类
    用Map或者直接switch应该是比较好的方法

    第 3 条附言  ·  2019-12-31 14:42:20 +08:00

    Integer unit = getUnit (type) 这个方法是从一个远古软件生成的txt文档中提取 单位(数字格式)
    不同的unit对应什么含义,需要查阅PDF文档来翻译,有很多不同情况
    大家不要纠结为什么不统一unit与transferredUnit的对应关系哈,这个我无能为力...

    57 条回复    2020-01-01 18:31:04 +08:00
    avk458
        1
    avk458  
       2019-12-31 11:25:38 +08:00
    三元表达式还能这样玩儿?
    按照这个`codeMeaning`的意思来用 switch 是不是更好?
    包装类比较是不是应该用 equals ?
    pmispig
        2
    pmispig  
       2019-12-31 11:27:02 +08:00   ❤️ 7
    谁写的,先把他头锤爆
    Lin0936
        3
    Lin0936  
       2019-12-31 11:28:53 +08:00
    // TODO: Bullshit
    sandman511
        4
    sandman511  
    OP
       2019-12-31 11:34:59 +08:00
    @pmispig 实不相瞒。。我写的。。
    manami
        5
    manami  
       2019-12-31 11:36:53 +08:00 via Android
    Objects.equals(a, b)
    optional
        6
    optional  
       2019-12-31 11:37:00 +08:00   ❤️ 1
    // 第一种提前返回
    ```
    if (Objects.isNull(code)) {
    return null;
    }
    //switch or if
    ```
    //第二种,
    ```
    Optional.ofNullable(code).map(code->{
    //switch or if
    }).orElse(null);
    ```
    passerbytiny
        7
    passerbytiny  
       2019-12-31 11:37:08 +08:00
    一,Java 没有指针;
    二,三元连接符嵌套使用是大忌;
    三,if(some == null) { //bulabula when null }else{ //bulabula when not null},或者 if(some != null){bulabula},或者 some.ifPresent((value)->{/bulabula}), 是良好的编码习惯;
    四,请善用默认值:
    Integer code = getCode();
    String codeMeaning == null ;// 或者 "默认值"
    if(code != null){
    switch (code.intValue()){
    case 1 : codeMeaning = "你好"; break;
    // ......
    }
    }
    sandman511
        8
    sandman511  
    OP
       2019-12-31 11:38:17 +08:00
    @avk458 我嫌 switch 写起来太麻烦了 这个逻辑比较简单 就三行 所以用了三元。。。请教一下三元不能这样用吗?
    确实该用 equals,但是 code 可能为 null,equals 可能抛空指针。。所以不知道该咋办了
    lff0305
        9
    lff0305  
       2019-12-31 11:40:35 +08:00   ❤️ 2
    ```
    Optional<Integer> code = Optional.ofNullable(getCode());
    String s = code.map(c -> {
    switch (c) {
    case 1: return "one";
    case 2: return "two";
    }
    return "other";
    }).orElse(null);
    ```
    ily433664
        10
    ily433664  
       2019-12-31 11:44:24 +08:00
    Map<Integer, String> map = new ....
    codeMeaning = map.get(code);
    littleylv
        11
    littleylv  
       2019-12-31 11:44:46 +08:00
    实不相瞒,三元这么嵌套不加括号的,除了傻逼笔试题外,工作中这样写的同事我会打死他(不是不能嵌套,你得加括号增加可读性)
    lihongjie0209
        12
    lihongjie0209  
       2019-12-31 11:54:00 +08:00
    不加括号就是作死
    w292614191
        13
    w292614191  
       2019-12-31 11:55:22 +08:00
    我感觉比楼上那些 if else switch 好看多了。

    都是些秀无意义代码的。

    你看看 7、9 楼,

    硬是加长代码,毫无意义。
    Jimmy2Angel
        14
    Jimmy2Angel  
       2019-12-31 11:58:35 +08:00
    @passerbytiny 一,你真是个合格的杠精
    ipwx
        15
    ipwx  
       2019-12-31 12:01:09 +08:00
    private static final String[] lookup = {null, "你好", "你好好", "你好好好"};
    String codeMeaning = lookup[code != null? code.intValue() : 0];

    https://ideone.com/7rXSyK
    matepi
        16
    matepi  
       2019-12-31 12:11:15 +08:00 via iPhone
    enum 特性都多少年了吧
    banmuyutian
        17
    banmuyutian  
       2019-12-31 12:11:53 +08:00   ❤️ 1
    @w292614191
    “硬是加长代码,毫无意义。”?
    等你看到那些难以阅读维护的代码就知道代码规范的重要性了
    micean
        18
    micean  
       2019-12-31 12:12:24 +08:00
    挺清晰的,加啥括号?有换行和缩进还不够吗?
    lhx2008
        19
    lhx2008  
       2019-12-31 12:14:49 +08:00 via Android
    enum 吧,或者 map
    code 的话,null 就整个-1 呗,null 来 null 去的
    wysnylc
        20
    wysnylc  
       2019-12-31 12:18:23 +08:00
    optional+switch,或者用 java 13 还是 14 的新 switch
    kkkkkrua
        21
    kkkkkrua  
       2019-12-31 12:20:02 +08:00 via iPhone
    switch 不就好了么,用 default 处理 null
    sagaxu
        22
    sagaxu  
       2019-12-31 12:33:11 +08:00 via Android
    应该用 int,避免 Integer,code 增加一项表示原来用 null 表示的情况。其实用 enum 不是更好吗?
    Sqpan2
        23
    Sqpan2  
       2019-12-31 12:35:54 +08:00 via Android
    做个枚举不香吗?
    sandman511
        24
    sandman511  
    OP
       2019-12-31 12:43:05 +08:00
    @Sqpan2
    @sagaxu
    @lhx2008
    @matepi
    不能用枚举哈,因为 code=1 不一定代表就是”你好“ 只是在这个地方等于”你好"
    在另一个方法里可能就是”你好好好“
    除非创建 7、8 个枚举类,每个枚举类中就三个参数。。
    oneisall8955
        25
    oneisall8955  
       2019-12-31 13:04:03 +08:00 via Android
    var table=new HashMap<Integer,String>(){
    {
    put(k1,v1);
    put(k2,v2);
    // ...
    }
    };

    table.get(xx)
    zzzHas
        26
    zzzHas  
       2019-12-31 13:04:07 +08:00
    如果能提高阅读性, 代码长点就长点.
    looseChen
        27
    looseChen  
       2019-12-31 13:26:14 +08:00
    别特么的玩些花里胡哨的玩意
    wesnow
        28
    wesnow  
       2019-12-31 13:26:40 +08:00 via Android
    代码更容易阅读比代码的长短重要得多,甚至可以说除非是非常极端的情况(比如有个地方确实对性能要求很高,这里就可能牺牲部分阅读性),否则都应该把代码更容易阅读更容易理解放在第一位。
    qyvlik
        29
    qyvlik  
       2019-12-31 13:43:16 +08:00
    @ipwx #15 这种查表法可还行。省去 if, switch 的逻辑判断。
    wc951
        30
    wc951  
       2019-12-31 13:45:54 +08:00 via Android
    java8 的 optional 是最优雅的
    1et
        31
    1et  
       2019-12-31 13:50:43 +08:00
    int a = 1,b=2,c=3;
    int d = a+++b+++c;
    palmers
        32
    palmers  
       2019-12-31 13:59:27 +08:00
    按照你回复的#24 我觉得可以使用枚举区分调用类别, 然后返回字符串抽象为方法 应该可以达到你的目的, 这么用确实不太好 除了秀也没有达到简洁的目的, 维护会越来越乱 临时用一下就没什么了
    不知道你真是的需求是什么 就目前你暴露的意愿 我觉得 枚举+抽象可以很好的解决这个问题 也符合开闭原则
    ymz
        33
    ymz  
       2019-12-31 14:09:37 +08:00
    @kkkkkrua null 的话报空指针错误,到不了 default
    guyeu
        34
    guyeu  
       2019-12-31 14:10:15 +08:00   ❤️ 1
    String result = switch (Objects.requireNonNullElse(code, 0)) {
    case 0 -> null;
    case 1 -> "你好";
    case 2 -> "你好好";
    case 3 -> "你好好好";
    default -> throw new UnsupportedOperationException();
    };
    kkkkkrua
        35
    kkkkkrua  
       2019-12-31 14:12:16 +08:00
    @ymz #33 学到了,谢谢
    hhhsuan
        36
    hhhsuan  
       2019-12-31 14:21:15 +08:00
    代码的首要作用是给人看,只是随便可以运行一下。
    oaix
        37
    oaix  
       2019-12-31 14:24:57 +08:00
    Optional 一般只建议用在方法的返回值。
    ilumer
        38
    ilumer  
       2019-12-31 14:29:36 +08:00
    optional+enum 或者 optional+map 在 map 里面写 if else 的逻辑
    sandman511
        39
    sandman511  
    OP
       2019-12-31 14:30:16 +08:00
    @palmers 我又在主贴 APPEND 了一下真正的需求
    earther01
        40
    earther01  
       2019-12-31 14:39:05 +08:00
    @oaix 为啥呢,是效率不高吗?
    oaix
        41
    oaix  
       2019-12-31 14:47:05 +08:00
    wutiantong
        42
    wutiantong  
       2019-12-31 14:50:57 +08:00
    从 append 的代码来看,你应该把 Integer unit + String transferredUnit 这两样东西 wrap 成一个简单的 class,不妨就还叫 unit 好了,数字和字符串作为 unit 这个 class 的两个成员属性。

    你的 getUnit(type) 应该直接返回这个 unit class。
    palmers
        43
    palmers  
       2019-12-31 15:25:29 +08:00   ❤️ 1
    public enum UnitOfWeight {
    KG("kg") {
    @Override
    public boolean match(int unit) {
    return false;
    }
    },
    GRAM("g") {
    @Override
    public boolean match(int unit) {
    return false;
    }
    },
    TON("t") {
    @Override
    public boolean match(int unit) {
    return false;
    }
    },
    UNKNOWN("x-0"){
    @Override
    public boolean match(int unit) {
    return false;
    }

    @Override
    public boolean unknown() {
    return true;
    }
    };


    /**
    * e.g. kg, g, t
    */
    @Getter
    private String note;

    static UnitOfWeight[] units = UnitOfWeight.values();

    /**
    * kg's enums
    */
    static int[] kgs = {1, -1, 4};
    /**
    * gram's enums
    */
    static int[] grams = {2, 0, 5};
    /**
    * ton's enums
    */
    static int[] tons = {3, 1, 6};

    UnitOfWeight(String note) {
    this.note = note;
    }


    public static UnitOfWeight valueOf(Integer unit) {
    for (UnitOfWeight ofWeight : units) {
    if (unit == null) {
    return UNKNOWN;
    }
    if (ofWeight.match(unit)) {
    return ofWeight;
    }
    }
    return UNKNOWN;
    }


    /**
    * 获取对应枚举
    * @param unit
    * @return
    */
    public abstract boolean match(int unit);

    /**
    * 错误的值
    * @return
    */
    public boolean unknown() {
    return false;
    }


    public static void main(String[] args) {
    Integer unit = null;
    //getUnit (type2);
    UnitOfWeight ofWeight = UnitOfWeight.valueOf(unit);
    if (!ofWeight.unknown()) {
    String transferredUnit = ofWeight.getNote();
    //...
    }
    }
    }

    根据你 append 的代码用了一个枚举来做, 根据你的描述我想有几个点需要扩展,
    1. 从 pdf 解析 type 值;
    2. 有很多情况(可能因为历史原因很多地方使用了不同的值代表某一个单位);
    所以我建议使用枚举, 原因是:
    1. 我觉得后面随着业务需要或者其他原因重构, 可能慢慢的这些代表某一个单位的值会改变或者统一为一个,所以你需要修改 通过枚举可以统一在一处,便于修改;
    2. 再者也方便返回的字符值变更 总之对于变更这个枚举类基本上可以应对 90%以上的情况;
    3. match 方法可以做针对行的扩展和优化;
    boileryao
        44
    boileryao  
       2019-12-31 16:20:59 +08:00
    ```java
    // Put this map somewhere
    Map<Integer, String> codeMeaningInfo = new HashMap<>();
    codeMeaningInfo.put(1, "你好");
    codeMeaningInfo.put(2, "你好好");
    codeMeaningInfo.put(3, "你好好好");

    // Using this map
    String codeMeaning = codeMeaningInfo.get(code);
    ```
    一般情况下 Map 应该全局唯一,另外推荐 Kotlin 的 `when` 表达式。
    loryyang
        45
    loryyang  
       2019-12-31 16:22:23 +08:00
    可以的话,尽量不要用 null,容易在各种传递运算中出现空指针异常。比如你这个 code 和 codeMeaning,code=-1,codeMeaning=“”表示异常。甚至可以用 Enum 来包装这些内容
    如果硬是要用 null,我建议把 null 的处理和其他处理分开。尽量不要用三元表达式,这种东西比较反人类,写起来很爽,看起来一头包。
    你这种情况可以写个函数 getCodeMeaning(Integer code) {
    if (code == null) {
    return null;
    } else if (code == 1) {
    ******
    }
    代码在可读可维护性面前,简洁性可以要求低一些
    luozic
        46
    luozic  
       2019-12-31 16:45:26 +08:00
    如果 java 有 pattern match 就可以玩优雅;但是 Java 没有,所以,理智一点,用表驱动去糊弄算了。
    redford42
        47
    redford42  
       2019-12-31 17:24:29 +08:00
    前几天看到一个 java8 的 optional
    但其实我没看太明白...
    loryyang
        48
    loryyang  
       2019-12-31 17:27:32 +08:00
    @loryyang #45 另外还有一个避免魔法值,前面也好多人提了。对于这一点,我是觉得,你考虑一下是否有必要做。你这种 case,直接用一个 ImmutableMap 好了,这样可以变成:
    ImmutableMap.<Integer, String>builder().put(1, "").put(2, "").build()
    然后 getCodeMeaning 函数里面变成
    if (code == null) {
    return null;
    }
    if (code in immutableMap) {
    return immutableMap.get(code);
    }
    else {
    return ***;
    }

    回头看一下,还是 null 太多了,null 这东西还是不要在正常流程中出现吧
    nekoneko
        49
    nekoneko  
       2019-12-31 17:33:15 +08:00
    @redford42 #47 感觉 optional 就是婊子立牌坊
    godoway
        50
    godoway  
       2019-12-31 18:16:00 +08:00 via Android   ❤️ 1
    @nekoneko
    @redford42
    这其实是语法上提醒你检测空安全,例如有个接口入参 optional,你就知道这里可能为空,入参一个对象有可能你就忘了检测非空
    godloveplay
        51
    godloveplay  
       2019-12-31 18:37:57 +08:00
    你写的代码是给别人看的。 请你站在别人的角度思考一下,看到这个代码的心情
    oneisall8955
        52
    oneisall8955  
       2019-12-31 19:25:42 +08:00 via Android
    @nekoneko 深层嵌套获取值,一直判断非空很啰嗦,Optional 可以解决这个问题
    winiex
        53
    winiex  
       2020-01-01 11:15:47 +08:00
    永远不要嵌套多层三元表达式,三元表达式当且仅当只有一层逻辑判断时才可以使用。其它情况一律用 if else 老实地写。我甚至连一层逻辑判断都想强制只能用 if else。
    brucefu
        54
    brucefu  
       2020-01-01 15:32:39 +08:00
    @micean 能让别人一秒就看懂的东西,就不要搞成两秒
    micean
        55
    micean  
       2020-01-01 16:49:23 +08:00
    @brucefu

    code == null ? null :
    code == 1 ? "你好" :
    code == 2 ? "你好好" :
    code == 3 ? "你好好好" :
    "其他";

    这不就是一个简易秒懂的 switch,楼上其他代码也做不到这么工整
    micean
        56
    micean  
       2020-01-01 16:55:50 +08:00
    另外嵌套的三元还可以轻轻松松给 final 修饰的赋值

    final String name =

    code == null ? "" :
    code == 1 ? "你好" :
    code == 2 ? "你好好" :
    code == 3 ? "你好好好" :
    "其他";
    secondwtq
        57
    secondwtq  
       2020-01-01 18:31:04 +08:00
    这不就是一个 Maybe Integer 解决的问题 ... 搞这么复杂
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3724 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 10:49 · PVG 18:49 · LAX 03:49 · JFK 06:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.