2017-09-27 48 views
2

我遇到過在重構代碼時或多或少看起來像這樣的情況;這裏改寫爲示範起見:Java構造函數與方法參考鏈接

public class UrlProbe { 
    private final OkHttpClient http; 
    private final String url; 
    private final Function<Response, Object> transform; 
    private Object cachedValue; 

    public UrlProbe(OkHttpClient http, String url) { 
     this(http, url, this::debuggingStringTransform); 
    } 

    public UrlProbe(OkHttpClient http, String url, Function<Response, Object> transform) { 
     this.http = http; 
     this.url = url; 
     this.transform = transform; 
    } 

    private Object debuggingStringTransform(Response response) { 
     String newValue = response.body().toString(); 
     System.out.println("Debugging new value from url " + url + ": " + newValue); 
     return newValue; 
    } 

    public synchronized Object probe() { 
     if (cachedValue != null) { 
      return cachedValue; 
     } 

     try (Response response = http.newCall(new Request.Builder().url(url).get().build()).execute()) { 
      cachedValue = transform.apply(response); 
      return cachedValue; 

     } catch (IOException e) { 
      throw new UncheckedIOException(e); 
     } 
    } 
} 

此代碼不能編譯,因爲我們cannot reference this before supertype constructor has been called

public UrlProbe(OkHttpClient http, String url) { 
    this(http, url, this::debuggingStringTransform); 
} 

下面將無法編譯之一:

public UrlProbe(OkHttpClient http, String url) { 
    this(http, url, response -> debuggingStringTransform(response)); 
} 

唯一的辦法我我們發現這是明確使用非鏈式構造函數:

public UrlProbe(OkHttpClient http, String url) { 
    this.http = http; 
    this.url = url; 
    this.transform = this::debuggingStringTransform; 
} 

儘管在構造函數鏈接參數中限制使用this是有意義的,但我發現它在這個特定的上下文中令人驚訝,因爲似乎沒有任何形式的對使用this當涉及到方法引用和lambda表達式的內容時。

除了這是因爲JLS§8.8.7.1這麼說嗎?

+0

你也可以使'debuggingStringTransform'方法靜態並使用靜態方法參考 –

+0

@VladBochenin我在工作中遇到的情況比這更復雜一點,但在我的例子'debuggingStringTransform'中我注意包含一個引用到實例字段'url',以便它不能是靜態的 – Hay

+0

這是因爲在調用super或super之前不能保證調用debugginStringTransform。如果下一個構造函數在執行super之前調用函數呢? – jontro

回答

2

允許引用此範圍內的早期會破壞代碼,看起來像這樣

public class UrlProbe { 
    final String url; 
    final String param2; 
    public UrlProbe(String url) { 
     this(url, this::debuggingStringTransform); 
    } 

    public UrlProbe(String url, Function<String, String> transform) { 
     this(url, transform.apply("")); // <-- What should happen when url is referenced here? 

    } 
    public UrlProbe(String url, String param2) { 
     this.url = url; 
     this.param2 = param2; 
    } 

    private String debuggingStringTransform(String response) { 
     System.out.println("Debugging new value from url " + url + ": " + response); 
     return response; 
    } 
} 

這至少是違反規則的一種方式。

+0

這很有趣,我絕對沒有想到被調用的構造函數可以調用作爲參數傳遞的函數(並且可能將其鏈接到另一個構造函數)。 – Hay

1

的IntelliJ顯示了這個在提示爲什麼這個代碼是「壞」:

enter image description here

不能引用「這個」超之前的構造被稱爲

這是有道理的。你正在構建你的對象,並且定義的方法引用僅在之後存在,該類被實例化。它不能現實存在之前它是完全實例化的,因爲從語義的角度來看,你不能對它做任何事情,直到它「準備就緒」。

如果你想解決這個問題,你可能變化其功能是靜態的,因爲沒有它所需的狀態:

public UrlProbe(OkHttpClient http, String url) { 
    this(http, url, UrlProbe::debuggingStringTransform); 
} 

private static Object debuggingStringTransform(Response response) { 
    String newValue = response.body().toString(); 
    System.out.println("Debugging new value from url " + url + ": " + newValue); 
    return newValue; 
} 

...但無可否認它看起來怪異到請參閱private static方法。

另外,有此功能在同一封裝在一個靜態類在這一塊的底部存在別處,如:

public UrlProbe(OkHttpClient http, String url) { 
    this(http, url, Functions::debuggingStringTransform); 
} 

static class Functions { 
    static Object debuggingStringTransform(Response response) { 
     String newValue = response.body().toString(); 
     System.out.println("Debugging new value from url " + url + ": " + newValue); 
     return newValue; 
    } 
}