Symbole wieloznaczne Java Generics z wieloma klasami


385

Chcę mieć obiekt klasy, ale chcę wymusić dowolną reprezentowaną przez niego klasę, aby rozszerzyć klasę A i wdrożyć interfejs B.

Potrafię:

Class<? extends ClassA>

Lub:

Class<? extends InterfaceB>

ale nie mogę zrobić obu. Czy jest na to sposób?

Odpowiedzi:


613

W rzeczywistości możesz robić, co chcesz. Jeśli chcesz udostępnić wiele interfejsów lub interfejsów plus klasa, musisz mieć wygląd wieloznaczny mniej więcej tak:

<T extends ClassA & InterfaceB>

Zobacz samouczek ogólny na stronie sun.com, w szczególności sekcję Parametry typu ograniczenia na dole strony. Jeśli chcesz, możesz podać więcej niż jeden interfejs, używając & InterfaceNamekażdego z nich.

Może się to arbitralnie skomplikować. Aby to zademonstrować, zobacz deklarację JavaDoc Collections#max, która (zawinięta w dwie linie) to:

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

dlaczego tak skomplikowane? Jak powiedziano w Java Generics FAQ: Aby zachować zgodność binarną .

Wygląda na to, że nie działa to w przypadku deklaracji zmiennych, ale działa, gdy nakłada się ogólną granicę na klasę. Tak więc, aby zrobić to, co chcesz, możesz skoczyć przez kilka obręczy. Ale możesz to zrobić. Możesz zrobić coś takiego, ustanawiając ogólną granicę dla swojej klasy, a następnie:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

aby uzyskać, variableże ma ograniczenie, które chcesz. Więcej informacji i przykładów można znaleźć na stronie 3 Ogólnych w Javie 5.0 . Zauważ, że w <T extends B & C>, nazwa klasy musi być pierwsza, a interfejsy następują. I oczywiście możesz wymienić tylko jedną klasę.


94
To jest pomocne. Warto wspomnieć, że klasa musi być na pierwszym miejscu, nie można powiedzieć „<T przedłuża interfejs B i klasa A>”.
EricS

5
Jak to zrobić w przypadku T, należy rozszerzyć klasę LUB zaimplementować interfejs?
Ragunath Jawahar

1
@RagunathJawahar: Nie możesz być zbyt otwarty na ten temat. Musisz mieć pewne granice, a jedna z nich z góry wie, gdzie będziesz mieć interfejsy i gdzie będziesz mieć zajęcia i jak będziesz korzystać z dziedziczenia. Musisz właściwie wiedzieć o parametrze typu, czy będzie to klasa, czy interfejs.
Eddie

4
Pierwszy wiersz odpowiedzi brzmi: „spraw, aby Twój symbol wieloznaczny wyglądał mniej więcej tak”. Nie ma ?w tym wyrażeniu, więc czy to naprawdę „symbol wieloznaczny”? Pytam, ponieważ nie mogę uruchomić całej koncepcji „rozszerzania dwóch rzeczy naraz”, gdy parametr type naprawdę jest symbolem wieloznacznym.
The111

1
Tak, to nie jest tak naprawdę symbol wieloznaczny i nie możesz zrobić tego, o co OP poprosił za pomocą symbolu wieloznacznego. Ty może robić to, co PO chciała, skutecznie, nie tylko z nazwy.
Eddie

17

Nie możesz tego zrobić z parametrami typu „anonimowymi” (tj. Używającymi symboli wieloznacznych ?), ale możesz to zrobić z parametrami typu „nazwanego”. Po prostu zadeklaruj parametr typu na poziomie metody lub klasy.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}

2
Dlaczego symbole wieloznaczne nie są dozwolone? tzn. nie rozumiem, dlaczego to nie powinno być prawidłowe: ArrayList <? rozszerza listę ClassA i InterfaceB>; A następnie, jeśli wyciągnąłeś element z listy, możesz przypisać go do zmiennej typu ClassA lub typu InterfaceB
Mark

Zastąpienie symbolu wieloznacznego zmienną to świetna technika! Ale to nie zawsze działa. Na przykład Java nie zezwala na zmienne typów nazw w typach wartości adnotacji, podczas gdy zezwala na symbole wieloznaczne.
sigpwned 22.07.17
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.