2012-11-15 87 views
3

我想知道是否有可能在Java中的方法範圍內引入類型變量。也就是說,在方法體內限制它們的範圍。在Java中的範圍有限的類型變量

但是,我並沒有試圖描述抽象中的問題,而是讓我用我的具體問題來說明問題。我有一對夫婦,看上去有點像這樣的類:

public class Settings { 
    public static abstract class Setting<T> { 
     public T value; 
     public abstract T fallbackvalue(); 
    } 

    private final List<Setting<?>> settings; 
} 

我現在想編寫一個函數,在Settings,所有設置的值設置爲自己的回退值由抽象方法提供。我首先想到的是做這樣的:

public void reset() { 
    for(Setting<?> setting : settings) 
     setting.value = setting.fallbackvalue(); 
} 

然而,在第二以爲這是相當明顯的,爲什麼這不起作用; setting.value的捕獲<?>setting.fallbackvalue()的捕獲不同。艾爾戈,我需要一些方法來統一捕捉。這是可能解決這個問題是這樣的:

private static <T> void reset1(Setting<T> s) { 
    s.value = setting.fallbackvalue(); 
} 

public void reset() { 
    for(Setting<?> setting : settings) 
     reset1(setting); 
} 

顯式類型變量<T>reset1統一了捕獲方便,但它顯然是世界上最醜陋的東西來介紹這個功能,污染命名空間,雜亂只是爲了滿足類型系統而使代碼不易讀取。

難道我不能在reset的身體內做到這一點嗎?我想要做的僅僅是這樣的:

public void reset() { 
    for(Setting<?> setting : settings) { 
     <T> { 
      Setting<T> foo = setting; 
      foo.value = foo.fallbackvalue(); 
     } 
    } 
} 

這不是在世界上最漂亮的事情,但至少在我眼裏它比上面的變異遠不如strainful。唉,這是不可能的;但是什麼可能呢?

回答

2

沒有...

儘管通配符捕獲的確引入了新的類型變量,但它們只對編譯器可用;程序員無法直接訪問它們。

目前,只有類/方法可以引入類型變量。因此,爲了表達型帶通配符轉換爲類型沒有通配符的唯一途徑是通過一種方法(這是基本相同的機構或與金剛石推理new Foo<>(setting)一個構造)

+0

恐怕我不得不承認你是對的。更多的搜索似乎通常提出「reset1」方法作爲解決問題的方式。這太糟糕了。 – Dolda2000

3

在沒有改變代碼的其他方面的情況下,你無法做到你所要求的。然而(爲您解決特定問題),你可以在內部Setting類寫reset方法:

public void reset() { 
    value = fallbackvalue(); 
} 

然後你的循環(在Settings類的reset方法),簡直是:

for (Setting<?> setting : settings) 
    setting.reset(); 
+0

當然,但這和'reset1'函數基本上是一樣的,除了'Settings.reset'的內部實現細節之外,它沒有任何用處。無論哪種方式,我都對類型捕獲管理的更普遍問題感興趣。 – Dolda2000

+0

@ Dolda2000是的*基本上*'reset1',但是更合理。最終,我認爲這是最好/最乾淨/最容易管理/最易讀的解決方案。此外,我不確定你的問題可以通過其他方式解決(但我可能是錯的 - 這是以前發生的)。如果我想到一種不太複雜的方式(即不要求修改代碼的其他方面),我一定會更新我的答案。 – arshajii

+0

當然,但問題不在於如何正確模擬白話OOP中的示例概念,而是關於類型捕獲管理。 ;) – Dolda2000

1

reset1事情是普遍接受的表達方法來做到這一點。它被稱爲「捕捉幫助者」,並且是泛型中經常被引用的模式。它通常在像下面這樣的情況出現:

public void swap(List<?> list, int i, int j) { // swap elements i and j of the list 
    // how to write? 
} 

在這種情況下,你需要在兩個得到的東西的參數化類型,並把東西背在這種類型的。通配符不會讓你這樣做。這就像你的情況一樣,因爲你也得到了一些東西並且加入了一些東西。我們知道類型必須是相同的,但通配符太弱而無法執行。只有一個明確的類型變量可以讓我們做到這一點:

public <T> void swap(List<T> list, int i, int j) { 
    T tmp = list.get(i); 
    list.set(i, list.get(j)); 
    list.set(j, tmp); 
} 

但是,我們不希望這個外來<T>是在參數列表中只在一個地方只使用。對外界來說,swap(List<?> list, int i, int j)應該工作得很好。我們需要使用這個<T>類型參數是一個沒有人需要知道的實現細節。因此,要隱藏它,我們與需要通配符功能包的通用功能:

private <T> void swap_private(List<T> list, int i, int j) { ... } 
public void swap(List<?> list, int i, int j) { 
    swap_private(list, i, j); 
} 

它似乎浪費了,但是這是怎麼回事。

鑑於你的情況和這一情況之間的相似情況,以及捕捉幫助者在這種情況下是規範解決方案的事實,我可以充滿信心地告訴你,不,沒有更好的方法來做到這一點。