Biorąc pod uwagę dwa następujące definicje klasy:Parametryzacja Dobrze ukształtowania i przechwytywania konwersji w Javie
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
I następującą deklarację typu:
C1<C2<?>> a;
Intuicyjnie czuje deklarowany typ a
powinien być ważny, ale to nie jest sposób w jaki zachowuje się JDK-8u45. Zamiast tego mamy coś jak poniżej:
Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
C1<C2<?>> a;
^
where T is a type-variable:
T extends C1<T> declared in class C1
1 error
(Edit: byłem bycie Dingus tutaj, ta część została odpowiedział: C2<?>
nie wykracza C1<C2<?>>
Zagadnienie dotyczące deklaracji c
poniżej. jest wciąż kwestią otwartą, choć.)
Ale C2<?>
ma przedłużyć C1<C2<?>>
, który wydaje się trywialnie zaspokoić związany. Badanie JLS nie zapewnia dalszego oświetlenia, o ile widzę. To naprawdę powinno być tak proste, jak spełnienie powiązania przez relację podtypu, ponieważ C2<?>
nie jest typem wieloznacznym, a zatem konwersja przechwytywania jest tylko konwersją tożsamości dla argumentu.
Istnieją sytuacje, w których staje się nieco mniej oczywiste, na przykład podjąć następujące definicje klasy:
class C3<T extends C3<?>> {}
class C4<Y, Z> extends C3<C4<Z, Y>> {}
class C5<X extends C3<X>> {
void accept(X x);
}
Wszystko to jest w porządku, ale jeśli spróbujemy następującą deklarację:
C5<C6<?, ?>> b;
Rzeczy stają się coraz dziwniejsze. C6<?, ?>
jest podtypem C3<C6<?, ?>>
, więc deklaracja powinna być ważna zgodnie z moją interpretacją specyfikacji podaną powyżej w odniesieniu do deklaracji C1<C2<?>>
. Problem polega na tym, że wyraźnie nie każdy możliwy podtyp C6<?, ?>
faktycznie spełnia to ograniczenie, więc teraz na przykład C5.accept()
rozstrzyga typ jego parametru na C6<?, ?>
, a więc może przyjmować argumenty, które naruszają granicę na X
, tj. Gdziekolwiek, gdzie parametryzacje Y
i Z
nie są identyczny.
Gdzie ja się tu mylę? Czy moje rozumienie związku podtypów jest niewystarczające?
(Edit: Poniższa część pytania jest nadal bez odpowiedzi, ale ja przeniósł go do nowego pytania here ponieważ jest to zupełnie inna kwestia naprawdę ... Przepraszam za bałagan i nie korzystania z witryny bardzo dobrze haha ...)
Poza tym, mam również problemy z konwersją przechwytywania w podobnych sytuacjach. Podjąć następujące oświadczenie typu:
C1<? extends C2<?>> c;
przeciwieństwie do podobnej deklaracji a
na początku, to kompiluje grzywny w JDK-8u45. Jeśli jednak przeanalizujemy kod specification for capture conversion, to wydaje się, że ta deklaracja powinna spowodować, że w tym czasie wystąpi błąd czasu kompilacji.
W szczególności, górna granica nowego typu zmienna przechwytywania CAP#T
określał glb(Bi, Ui[A1:=S1,...,An:=Sn])
, gdzie w tym przypadku Bi
postanawia wildcard związany C2<?>
i Ui[A1:=S1,...,An:=Sn]
postanawia C1<CAP#T>
.
Z tego glb(C2<?>, C1<CAP#T>)
postanawia typu przecięcia C2<?> & C1<CAP#T>
, które jest nieważne, ponieważ C2<?>
i C1<CAP#T>
są oba typy klas, a nie interfejs typu, ale ani jedna z nich nie jest podtypem drugiej.
To (pozorne) naruszenie przepisów jest bardziej jasne w samym definition of the intersection type.
Jestem pewien, że to nie jest błąd i po prostu robię gdzieś proste błędy ... ale jeśli nikt tutaj nie może mi o tym powiedzieć, wypróbuję listę mailingową kompilatora lub coś podobnego.
Dzięki za pomoc!
To fascynujący problem. Jestem po prostu ciekawy, czy to jest jakiś teoretyczny problem, czy masz jakieś zajęcia z prawdziwego świata? Po prostu nie mogę sobie wyobrazić, gdzie można by użyć 'class C1> {}' lub 'class C2 rozszerza C1 > {}'. –
Kris
Ważne pytanie! Nie mam żadnego konkretnego zastosowania tego konkretnego problemu w świecie ... Ale ja * muszę * zrozumieć właściwe oczekiwane zachowanie, ponieważ piszę zestaw narzędzi odblaskowych nad systemem typów i chcę, aby zachowanie pasowało do specyfikacji/JDK. Obecnie jest w większości działa, w tym typ wnioskowania ogólnego wywołania metody, ale istnieje kilka przypadków krawędzi (takich jak ten), które powodują problemy. Jeśli chcesz się przyjrzeć, jest tu hostowany: https://github.com/StrangeSkies/uk.co.strangeskies. Nie ma jeszcze wiki, ale javadocs są obecne w pakiecie '.reflection'. –
To ma bardzo niewiele wspólnego z konwersją przechwytywania. Zamiast tego powinieneś szukać odpowiedzi w 4.10.2 i 4.5.1. Jesteś wprowadzany w błąd tak długo, jak postrzegasz to jako związane z konwersją przechwytywania. Na przykład twoje uzasadnienie, dlaczego "C1 extends C2 >> 'nie powinno się kompilować to nonsens. (Myślę, że jest to również błędne, nie sądzę, że istnieje typ przecięcia, jeśli konwersja przechwytywania * została zastosowana *, ponieważ [konwersja przechwytywania nie zostałaby zastosowana rekurencyjnie] (http://stackoverflow.com/a/30385343/2891664) ale właśnie się obudziłem, więc jestem trochę oszołomiony.) – Radiodef