Mam ogólny interfejs
public interface Consumer<E> {
public void consume(E e);
}
Mam klasę, która zużywa dwa typy obiektów, więc chciałbym zrobić coś takiego:
public class TwoTypesConsumer implements Consumer<Tomato>, Consumer<Apple>
{
public void consume(Tomato t) { ..... }
public void consume(Apple a) { ...... }
}
Najwyraźniej nie mogę tego zrobić.
Mogę oczywiście samodzielnie zrealizować wysyłkę np
public class TwoTypesConsumer implements Consumer<Object> {
public void consume(Object o) {
if (o instanceof Tomato) { ..... }
else if (o instanceof Apple) { ..... }
else { throw new IllegalArgumentException(...) }
}
}
Ale szukam rozwiązania do sprawdzania typów i wysyłania w czasie kompilacji, które zapewniają produkty generyczne.
Najlepszym rozwiązaniem, jakie przychodzi mi do głowy, jest zdefiniowanie oddzielnych interfejsów, np
public interface AppleConsumer {
public void consume(Apple a);
}
Wydaje mi się, że funkcjonalnie to rozwiązanie jest w porządku. Jest po prostu gadatliwy i brzydki.
Jakieś pomysły?
java
generics
interface
multiple-inheritance
daphshez
źródło
źródło
Odpowiedzi:
Rozważ hermetyzację:
Jeśli przeszkadza Ci tworzenie statycznych klas wewnętrznych, możesz użyć klas anonimowych:
źródło
TwoTypesConsumer
spełnia żadnych umów, więc po co? Nie można go przekazać do metody, która potrzebuje dowolnego typuConsumer
. Cała idea konsumenta dwojakiego polega na tym, że można go podarować metodzie, która chce konsumenta pomidorów, a także metodzie, która chce konsumenta jabłek. Tutaj nie mamy żadnego.TwoTypesConsumer
razie potrzeby miały dostęp do otaczającej instancji, a następnie można było przejśćtwoTypesConsumer.getAppleConsumer()
do metody, która chce konsumenta Apple. Inną opcją byłoby dodanie metod podobnychaddConsumer(Producer<Apple> producer)
do TwoTypesConsumer.ExceptionMapper
) ...Ze względu na wymazywanie typów nie można dwukrotnie zaimplementować tego samego interfejsu (z różnymi parametrami typu).
źródło
Oto możliwe rozwiązanie oparte na rozwiązaniu Steve McLeod :
Implikowanym wymaganiem pytania było
Consumer<Tomato>
iConsumer<Apple>
obiekty, które mają wspólny stan. PotrzebaConsumer<Tomato>, Consumer<Apple>
obiektów wynika z innych metod, które oczekują ich jako parametrów. Potrzebuję jednej klasy, aby wdrożyć je obie w celu udostępnienia stanu.Pomysł Steve'a polegał na użyciu dwóch klas wewnętrznych, z których każda implementowała inny typ ogólny.
Ta wersja dodaje metody pobierające dla obiektów, które implementują interfejs konsumenta, które mogą być następnie przekazywane do innych metod, które ich oczekują.
źródło
Consumer<*>
instancje w polach instancji, jeśliget*Consumer
jest często wywoływany.Przynajmniej możesz dokonać niewielkich ulepszeń w implementacji wysyłki, wykonując coś takiego:
Owoc będący przodkiem Pomidora i Jabłka.
źródło
po prostu się na to natknąłem. Tak się złożyło, że miałem ten sam problem, ale rozwiązałem go w inny sposób: właśnie stworzyłem nowy interfejs, taki jak ten
niestety jest to uważane
Consumer<A>
zaConsumer<B>
sprzeczne z całą logiką. Musisz więc stworzyć mały adapter dla drugiego konsumenta w swojej klasiejeśli
Consumer<A>
jest potrzebne, możesz po prostu przejśćthis
, a jeśliConsumer<B>
to konieczne, po prostu przejśćconsumerAdapter
źródło
Nie można tego zrobić bezpośrednio w jednej klasie, ponieważ poniższa definicja klasy nie może zostać skompilowana z powodu usunięcia typów ogólnych i zduplikowanej deklaracji interfejsu.
Każde inne rozwiązanie pakowania tych samych operacji konsumpcji w jednej klasie wymaga zdefiniowania Twojej klasy jako:
co jest bezcelowe, ponieważ musisz powtórzyć / powielić definicję obu operacji i nie będzie do nich odwołań z interfejsu. IMHO robienie tego jest złym, małym i duplikatem kodu, którego staram się uniknąć.
Może to wskazywać również na to, że w jednej klasie jest zbyt duża odpowiedzialność za zużywanie 2 różnych obiektów (jeśli nie są one połączone).
Jednak to, co robię i co możesz zrobić, to dodać jawny obiekt fabryki, aby utworzyć połączonych konsumentów w następujący sposób:
Jeśli w rzeczywistości te typy są naprawdę sprzężone (powiązane), to polecam stworzyć implementację w taki sposób:
Zaletą jest to, że klasa fabryczna zna obie implementacje, istnieje stan współdzielony (w razie potrzeby) i w razie potrzeby można zwrócić więcej połączonych konsumentów. Nie ma powtarzającej się deklaracji metody konsumpcji, która nie pochodzi z interfejsu.
Należy pamiętać, że każdy konsument może być niezależną (nadal prywatną) klasą, jeśli nie jest w pełni spokrewniony.
Wadą tego rozwiązania jest złożoność wyższej klasy (nawet jeśli może to być jeden plik java) i aby uzyskać dostęp do metody consume, potrzebujesz jeszcze jednego wywołania, więc zamiast:
ty masz:
Podsumowując, możesz zdefiniować 2 ogólnych konsumentów w jednej klasie najwyższego poziomu przy użyciu 2 klas wewnętrznych, ale w przypadku wywołania musisz najpierw uzyskać odniesienie do odpowiedniego konsumenta implementującego, ponieważ nie może to być po prostu jeden obiekt konsumenta.
źródło
W stylu funkcjonalnym jest to dość łatwe bez implementowania interfejsu, a także sprawdza typ czasu kompilacji.
Nasz funkcjonalny interfejs do konsumpcji bytu
nasz menedżer do odpowiedniego przetwarzania i konsumowania podmiotu
źródło
Inna alternatywa, aby uniknąć stosowania większej liczby klas. (przykład przy użyciu java8 +)
źródło
Przepraszam, że odpowiadam na stare pytania, ale naprawdę to uwielbiam! Wypróbuj tę opcję:
Myślę, że właśnie tego szukasz.
Otrzymasz ten wynik:
źródło