2016-09-29 68 views
1

我想在java中的一些奇怪的泛型。在以下代碼中,foo上的替代適用,但bar上的替代不適用。我不明白爲什麼。用怪異的泛型在java方法中覆蓋錯誤

請注意,兩個覆蓋之間的唯一區別在於MB的約束。當覆蓋foo時,MB延伸Alpha<B, MB>,它的工作原理。當覆蓋barMB,延伸Beta<B, MB>並且它不起作用,即使Beta<B, MB>延伸Alpha<B, MB>

我認爲MB extends Beta<B, MB>Beta<A, MA> extends Alpha<A, MA>意味着MB extends Alpha<B, MB>。那爲什麼它不工作?

import java.util.function.Function; 

abstract class Alpha<A, MA extends Alpha<A, MA>> { 
    abstract public <B, MB extends Alpha<B, MB>> MB foo(Function<A, B> f); 
    abstract public <B, MB extends Alpha<B, MB>> MB bar(Function<A, B> f); 
} 

class Beta<A, MA extends Beta<A, MA>> extends Alpha<A, MA> { 
    @Override public <B, MB extends Alpha<B, MB>> MB foo(Function<A, B> f) { return null; } 
    @Override public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f) { return null; } 
} 
+1

[這裏是一個簡化版本](http://ideone.com/j9QkOG)同樣的問題。當您覆蓋泛型方法時,您無法更改類型參數的邊界。 – Radiodef

+1

相關http://stackoverflow.com/questions/23438813/cannot-override-generic-interface – Tunaki

+0

好的人,我現在明白了。總之,在改變泛型類型的邊界時,你不能重寫一個方法。期。無論您是否使用泛型類型和事件,無論如何使用它。 –

回答

2

不同的是,約束MB extends Beta<B, MB>是不一樣的MB extends Alpha<B, MB>;所以它不會覆蓋bar方法。

the error message says

Main.java:10: error: name clash: <B#1,MB#1>bar(Function<A#1,B#1>) in Beta and <B#2,MB#2>bar(Function<A#2,B#2>) in Alpha have the same erasure, yet neither overrides the other 
    @Override public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f) { return null; } 

所以,你需要改變你的通用約束,使Beta.bar確實覆蓋Alpha.bar

+0

謝謝。我明白這不是一個約束。但是MB中的限制更具體。它被用作返回類型。所以,Beta.bar無法返回任何與Alpha.bar不兼容的值。所以它應該沒問題。 –

+0

好的,我接受了你的答案,因爲它是最接近回答問題的答案。也許你應該更清楚地知道,如何使用泛型類型並不重要。 –

1

我預計MB擴展測試版< B,MB >和Beta < A,MA >延伸阿爾法< A,MA >將意味着MB擴展阿爾法< B,MB >。

雖然上述說法是正確的,所有MB擴展Beta<A, MB>還擴展Alpha<A, MB>,這不是問題。這兩個方法簽名

abstract public <B, MB extends Alpha<B, MB>> MB bar(Function<A, B> f); 

public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f); 

其實都是不同的!這些方法將接受不同的參數。假設我有一個class Gamma<A, MA extends Gamma<B, Gamma>> extends Alpha<A, MA>。根據抽象類Alpha中的規範,bar可能會返回類型爲Gamma的對象,因爲它是Alpha的子類。但是在barbarBeta的不能返回Gamma的類型,因爲Gamma不是Beta的子類。因此,這兩個方法簽名的行爲實際上會有所不同,從而使您在欄上的覆蓋無效!

+0

但是Beta.bar沒有義務返回Alpha.bar所能包含的內容。所以它不應該使覆蓋無效。您可以用一個新的實現覆蓋一個方法,該方法返回一個更具體的類型。 –

+0

我明白你的觀點。我的猜測是,無論隻影響返回參數的泛型參數,由於類型擦除Java不能允許泛型在覆蓋過程中被縮小,因爲它可能會影響其他不能縮小的類型(即參數或某些東西功能)。 –

+1

你有正確的想法,但它不是在這裏有問題的返回類型。 Covariant返回類型在Java中很好(參見[例如這裏](http://stackoverflow.com/a/1882587/2891664))。問題是特別的類型參數聲明。 – Radiodef