Java有陷阱,用時需謹慎——慎用入參作返回值

正常狀況下,在Java中入參是不建議用作返回值的。除了形成代碼不易理解、語義不清等問題外,可能還埋下了陷阱等你入坑。面試

問題背景

好比有這麼一段代碼:編程

@Named
public class AService {   
private SupplyAssignment localSupply = new SupplyAssignment();
    @Inject
    private BService bervice;

    public List<Supply> calcSupplyAssignment()
       List<Supply> supplyList = bService.getLocalSupplyList(this.localSupply);
        …
       return supplyList;
    }
}複製代碼

上面代碼,服務A但願調用服務B,以獲取supplyList,但同時,服務A又但願修改localSupply的狀態值,未能避免修改calcSupplyAssignment接口的(不想改返回的類型),將localSupply做爲了入參但同時也用做了返回值。設計模式

服務B代碼以下:編程語言

@Named
public class BService {   

public List<Supply> getLocalSupplyList (SupplyAssignment localSupply)
    SupplyAssignment supplyAssignment = this.getSupplyAssignment();
        // 但願localSupply被從新賦值後返回
        localSupply = supplyAssignment;
        …
        return supplyList;

    }
}複製代碼

在服務B代碼內部,服務A的入參localSupply被傳入,但願從新被supplyAssignment賦值然後返回新值。然而,這樣作是無效的。ide

問題緣由

先來看下編程語言中關於參數傳遞的類型:函數

  • 值傳遞(pass by value)是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中若是對參數進行修改,將不會影響到實際參數。
  • 引用傳遞(pass by reference)是指在調用函數時將實際參數的地址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。

由於Java程序設計語言是採用的值傳遞,由於Java沒有指針的概念。也就是說方法獲得的是全部參數值的一個拷貝,方法並不能修改傳遞給它的任何參數變量的內容。工具

所以,上述代碼中,服務A調用服務B時,服務B的參數localSupply其實是服務A的localSupply的一個拷貝,固然,這兩個都是指向了同一個地址對象supplyAssignment1。學習

file

當在服務B內部對參數localSupply進行從新賦值是localSupply = supplyAssignment,實際上,只是對B的參數localSupply作了重新賦值,B的參數localSupply會指向一個新的地址對象supplyAssignment2。this

file

從上圖能夠清晰看到,所以,服務A的localSupply和B的參數localSupply已經指向了不一樣的對象了,對B的參數localSupply作任何的修改,都不會影響服務A的localSupply的原值。這就是問題的緣由,你但願服務B來修改服務A入參的狀態,並將改後的值返回給服務A,但並不奏效。設計

解決方案

方案1:入參不要用做返回值

固然,這個是最清晰的且易於理解的,但這會致使有的接口的返回類型產生變化。

有時確實想要入參作返回值,那看方案2。

方案2:入參不要賦值新對象

這個方案就是直接在入參的對象上作狀態的修改,而不要去賦值新對象。仍是這個圖:

file在這個圖中,只要咱們是一直在B的參數localSupply修改的是supplyAssignment1的狀態值,那結果就能反饋到服務A的localSupply上。如何實現?看下下面代碼:

@Named
public class BService {   

    public List<Supply> getLocalSupplyList (SupplyAssignment localSupply)
        
        SupplyAssignment supplyAssignment = this.getSupplyAssignment();

        // 針對localSupply不能新建引用,只能從新賦值屬性
        BeanUtils.copyProperties(supplyAssignment, localSupply);
        …
        return supplyList;

    }

}複製代碼

在上面的方法中,咱們用到了Spring的工具類BeanUtils,該類的copyProperties方法的實質是將supplyAssignment的屬性值,賦值到了localSupply的屬性上。這意味着咱們是修改的B的參數localSupply上的屬性,而並未新建對象。

歡迎關注公衆號 【碼農開花】一塊兒學習成長 我會一直分享Java乾貨,也會分享免費的學習資料課程和麪試寶典 回覆:【計算機】【設計模式】【面試】有驚喜哦

相關文章
相關標籤/搜索