2013-06-30 15 views
11

W jaki sposób można uzyskać podobną funkcjonalność bez błędów?Wymuszanie ogólnych parametrów Java na takie same typy

class A<K> { 
    void f(K x) {} 
} 

void foo(A<? extends X> a, X x) { 
    a.f(x); // AN error: The method f(capture#1-of ? extends X) in the 
      // type A<capture#1-of ? extends X> is not applicable for the 
      // arguments (X) 
} 

wiem, że to się dzieje, ponieważ „a” może być instancją < „non-X”>, więc jego „F” nie musi przyjmować wystąpienie X jako parametr, ale jak Zmuszam parametry tego samego typu?

Oto więcej Kod:

klasa Test:

class Test { 
    <T> void foo(A<T> a, T x) { 
    a.f(x); // now it works! 
} 
} 

W pewnej klasy:

Container<X> container; 
public void test() { 
    X x = new X(); 
    new Test().foo(container.get(), x); 
} 

oto klasa pojemnik:

public class Container<K> { 
    A<? extends K> get() { 
    return new A<K>(); 
    } 
} 
+0

Czy X jest parametrem klasy lub typu? – Joni

Odpowiedz

17

Można siła parametr ers być tego samego typu wykonując następujące czynności:

// the first class, A<K>: 
class A<K> { 
    void f(K x) {} 
} 

// the second class, defining the method with generic type parameters 
class Test { 
    <T> void foo(A<T> a, T x) { 
    a.f(x); // now it works! 
    } 
} 

// a third class, that uses the above two: 
class Main { 
    public static void main(final String... args) { 
    final Test test = new Test(); 
    final A<String> a = new A<>(); 
    test.foo(a, "bar"); 
    } 
} 

Co to robi to: metoda foo określa ogólny typ parametru T i używa go do wymuszenia, że ​​parametr K typ klasy A musi pasować typ x, drugi parametr foo.

Możesz nawet nałożyć ograniczenia na <T>, jeśli chcesz i jeśli ma to sens dla Twojego problemu, na przykład <T extends Bar> void foo(A<T> a, T x) {...} lub z super. Jeśli chcesz, jeśli jako Joni zapytano o komentarz w pytaniu, X jest w rzeczywistości typem, a nie parametrem typu: użyłbyś <T extends X> void foo(...).


Po wyświetleniu kolejnego kodu problem staje się jasny.

Metoda .get() kontenera zwraca wystąpienie A<? extends K>. Dlatego parametr typu instancji uzyskanej z .get() nie jest w pełni określony. Zwykle nie jest to dobry projekt, aby zwrócić taki nieokreślony typ. Aby obejrzeć prezentację wideo z Joshua Bloch, autorem efektywnej Java oraz wieloma API i funkcjami w Javie, pokazującymi, jak ulepszyć taki interfejs API, sprawdź: http://www.youtube.com/watch?v=V1vQf4qyMXg&feature=youtu.be&t=22m. Dokładnie na 25'36 ", Joshua Bloch mówi:" nie próbuj ich używać [symboli wieloznacznych] przy zwracanych wartościach ", a on wyjaśnia to później.Zasadniczo, nie uzyskujesz większej elastyczności dzięki używaniu ich i po prostu tworzysz to boleśnie trudne dla użytkowników API, aby sobie z tym poradzić (po prostu poczuliście, jak to działa ...)

Aby to naprawić, wystarczy spróbować zmienić podpis .get() na A<K> get(), aby klasa kontenera będzie:

public class Container<K> { 
    A<K> get() { 
    return new A<K>(); 
    } 
} 

Ponieważ wiem, że get() zwraca instancję A<K>, nie ma powodów, by używać starszej sygnatura: to po prostu sprawia, że ​​można stracić informacji już znasz !

A jeśli to nadal nie działa, twój problem może być gdzieś indziej, a będziesz musiał pokazać jeszcze więcej kodu ... lub jeszcze lepiej, zadawać inne pytania!:)

+0

Ale w jaki sposób można go użyć? 'nowy test(). Foo (a, x);' nadal zwraca błąd: Metoda foo (A , T) w typie A . Test nie ma zastosowania dla argumentów (A , X) – Evgeny

+0

Zdaje się, że definiujesz klasę 'Test' jako wewnętrzną klasę' A', co nie jest dokładnie tym, co pokazałem powyżej. –

+0

To nie ma znaczenia: Błąd jest teraz: "Metoda foo (A , T) w teście typu nie ma zastosowania dla argumentów (A , X)' – Evgeny

6

pamiętając PECS rule, i biorąc pod uwagę sposób, w jaki używasz X, powinno być określenie jako niższy zamiast górna granica:

void foo(A<? super X> a, X x) 

W ten sposób żadne błędy kompilatora są produkowane, i masz najbardziej ogólny podpis, który ma zastosowanie.

+1

Nie jest to dokładnie to, o czym mówi pytanie: OP pyta o sposób, aby 'X' był * tym samym * typem zarówno na' A a' i 'X x'. Może on chce, co odpowiedziałeś, ale wtedy pytanie powinno zostać ponownie sformułowane. –

+0

OP również pyta "Jak można osiągnąć podobną funkcjonalność bez błędów?" - a odpowiedź Marko właśnie to robi. – meriton

+0

@meriton, rzeczywiście. Ale wydaje się to trochę zbyt niejasne, nieokreślone. –

Powiązane problemy