Mam kilka klas Java, które rozszerzają różne implementacje ogólnego interfejsu listy. Po prostu rejestrują wszystko, co jest dodawane do Listy.Jak mogę odwołać się do metody super w klasie Java, która implementuje interfejs, ale nie rozszerza innej klasy?
Lista LoggingArrayList jest pokazana poniżej. Jak sama nazwa wskazuje, rozszerza ona ArrayList. Klasa LoggingLinkedList jest identyczna, z wyjątkiem rozszerzenia LinkedList.
Moim głównym celem jest uniknięcie konieczności powielania całego wspólnego kodu, aby móc korzystać z innej klasy bazowej. Staram się przestrzegać zasady DRY (Nie powtarzaj się) w jak największym stopniu.
Przede wszystkim nie sugeruj lepszego sposobu logowania. To w ogóle nie jest moja prawdziwa aplikacja. To prosty sposób na pokazanie problemu, który mam.
Mam dwa ściśle powiązane pytania. Pierwszym jest pytanie w tytule. Jak mogę odwołać się do metody "super" w klasie Java, która implementuje interfejs, ale nie rozszerza innej klasy?
Klasa LoggingArrayList, jak pokazano poniżej, działa prawidłowo, ale kiedy zmienię deklarację klasy z ... rozszerza ArrayList na ... implementuje List, wtedy trzy odwołania do super.method() nie są już wywoływalne, stąd moje pierwsze pytanie .
Dobra odpowiedź na moje drugie pytanie prawie spowoduje pierwsze pytanie. Drugie pytanie brzmi: czy istnieje sposób zadeklarowania abstrakcyjnej klasy bazowej lub interfejsu poszerzającego listę o domyślne implementacje różnych metod add(), aby móc po prostu rozszerzyć tę abstrakcyjną klasę bazową lub zaimplementować ten interfejs i określić tylko jaki rodzaj Listy będzie podstawą konkretnej klasy?
Na przykład, chciałbym zrobić coś takiego:
interface LoggingList<T extends Object, L extends List<T>> extends L
{
// overloaded methods go here as shown below
// with overloaded methods declared as default for the interface
}
... wtedy mogę po prostu wdrożyć LoggingList jeden raz dla każdej konkretnej implementacji listy bez powielania wszystkich wspólnym kodzie. Klasy betonu może wtedy wyglądać tak, bez żadnego dodatkowego kodu potrzebnego w ich nawiasach klamrowych:
public class LoggingArrayList<T extends Object> implements LoggingList<T, ArrayList<T>> {}
public class LoggingLinkedList<T extends Object> implements LoggingList<T, LinkedList<T>> {}
Problem polega na tym, że definicja interfejsu jak zaproponowałem to jest nieważne (nie będzie kompilacji), a także, odniesienia do super.method w poniższym kodzie są niedostępne, chyba że zrobię LoggingList podklasą abstrakcyjną zamiast interfejsu, a potem skończę z powrotem tam, gdzie teraz jestem.
Z góry dziękuję za wszelkie pomysły na osiągnięcie mojego celu DRY.
Oto moja cała klasa LoggingArrayList.
public abstract class LoggingArrayList<T extends Object>
extends ArrayList<T>
{
protected void log(T e)
{
System.out.println(e == null ? "null" : e.toString());
}
@Override
public boolean add(T e) {
log(e);
// How do I reference a super.method()
// in a class that implements an interface
// but does not extend another class?
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends T> clctn) {
boolean anyChanges = false;
for(T e : clctn)
{
// ensure that we call our overridden version of add()
// so it gets logged.
anyChanges = anyChanges || add(e);
}
return anyChanges;
}
@Override
public boolean addAll(int i, Collection<? extends T> clctn) {
for(T e : clctn)
{
// ensure that we call our overridden version of add()
// so it gets logged.
add(i, e);
i++; // keep the inserted elements in their original order
}
return !clctn.isEmpty();
}
@Override
public T set(int i, T e) {
log(e);
// How do I reference a super.method()
// in a class that implements an interface
// but does not extend another class?
return super.set(i, e);
}
@Override
public void add(int i, T e) {
log(e);
// How do I reference a super.method()
// in a class that implements an interface
// but does not extend another class?
super.add(i, e);
}
}
Przedłużanie zbiorów to zawsze zła praktyka. W przypadku niektórych bardziej złożonych struktur istnieją również pułapki, których można uniknąć poprzez delegowanie zamiast rozszerzania. Spójrz na [ten artykuł na temat rozszerzania klas] (http://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html). – Dariusz
Artykuł dobrze ilustruje Twój punkt widzenia. W rzeczywistości rozważałem już potencjał tego problemu w przypadku dwóch metod AddAll. Zauważysz, że już zapewniam, że moja własna implementacja metody add() jest wywoływana, zamiast zakładać, że klasa bazowa zawsze będzie ją wywoływać. Sądzę, że podejście delegowane ma co najmniej tę zaletę, że nie musi zastępować żadnej z implementacji klasy bazowej ani przyjmować żadnych założeń na jego temat. Mogę po prostu dodać moje logowanie bez dbania o to, co rzeczywista lista robi wewnętrznie. – David