Niedawno zauważyłem, że istnieje możliwość posiadania statycznych metod w interfejsach. Podobnie jak w przypadku statycznych pól interfejsu, istnieje ciekawe zachowanie: nie są one dziedziczone.
Nie jestem pewien, czy jest to przydatne w rzeczywistych interfejsach, które mają zostać zaimplementowane. Umożliwia to jednak programistom tworzenie interfejsów, które są po prostu kopertami dla elementów statycznych, takich jak np. Klasy narzędzi.
Prostym przykładem jest po prostu koperta dla globalnych stałych. W porównaniu z klasą można łatwo zauważyć brakującą płytkę kotłową, public static final
ponieważ są one zakładane (co czyni ją mniej gadatliwą).
public interface Constants {
String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
int DEFAULT_TIMEOUT_MS = 30;
}
Możesz także zrobić coś bardziej złożonego, na przykład ten pseudo-wyliczenie kluczy konfiguracji.
public interface ConfigKeys {
static createValues(ConfigKey<?>... values) {
return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
}
static ConfigKey<T> key(Class<T> clazz) {
return new ConfigKey<>(clazz);
}
class ConfigKey<T> {
private final Class<T> type;
private ConfigKey(Class<T> type) {
this.type = type;
}
private Class<T> getType() {
return type;
}
}
}
import static ConfigKeys.*;
public interface MyAppConfigKeys {
ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
ConfigKey<String> COMPANY_NAME = key(String.class);
Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);
static values() {
return VALUES;
}
}
W ten sposób można również utworzyć „klasę” narzędzi. Jednak w narzędziach często przydatne jest stosowanie prywatnych lub chronionych metod pomocniczych, co nie jest całkiem możliwe w klasach.
Uważam, że jest to miła nowa funkcja, a zwłaszcza fakt, że elementy statyczne nie są dziedziczone, jest interesującą koncepcją, która została wprowadzona tylko do interfejsów.
Zastanawiam się, czy możesz uznać to za dobrą praktykę. Podczas gdy styl kodu i najlepsze praktyki nie są aksjomatyczne i jest miejsce na opinię, myślę, że zwykle istnieją uzasadnione powody, aby poprzeć opinię.
Bardziej interesują mnie powody (nie) używania takich wzorców.
Zauważ, że nie zamierzam implementować tych interfejsów. Są jedynie kopertą ze względu na ich statyczną zawartość. Zamierzam użyć tylko stałych lub metod i ewentualnie użyć importu statycznego.
default
metodach. Mówię ostatic
metodach i polach. Nie są one dziedziczone, więc nie przerywają wielokrotnego dziedziczenia.Odpowiedzi:
Umieszczanie stałych w interfejsach jest nazywane,
Constant Interface Antipattern
ponieważ stałe są często jedynie szczegółem implementacji. Dalsze wady zostały podsumowane w artykule Wikipedii na temat interfejsu Constant .Rzeczywiście, dokument Java stwierdza, że metody statyczne mogą ułatwić organizowanie metod pomocniczych, na przykład podczas wywoływania metod pomocniczych z metod domyślnych.
źródło
Jak stwierdzono w pytaniu, interfejs nie jest przeznaczony do implementacji (ani tworzenia instancji). Możemy więc porównać to z klasą, która ma prywatnego konstruktora:
super()
wywoływania, ale interfejs można „zaimplementować”new MyInterface() {};
To porównanie jest korzystne dla klasy, ponieważ pozwala na większą kontrolę. To powiedziawszy, klasa nie jest również przeznaczona do przechowywania rzeczy statycznych, można by oczekiwać, że będzie miała instancje. Cóż, nie ma idealnej opcji.
Dziwne jest również dziedziczenie po „interfejsie statycznym”:
Mogą to być powody, aby uznać to za zły wzorzec i nadal używać klas statycznych w tych przypadkach. Jednak dla kogoś uzależnionego od cukru syntaktycznego może to być kuszące nawet z wadami.
źródło
Jako @ MarkusPscheidt pomysł ten jest zasadniczo rozszerzeniem antipatternu Constant Interface . To podejście było początkowo szeroko stosowane, aby umożliwić dziedziczenie jednego zestawu stałych przez wiele różnych klas. Powodem, dla którego uznano to za antypattern, były problemy, które napotkano przy stosowaniu tego podejścia w rzeczywistych projektach. Nie widzę powodu, aby sądzić, że problemy te dotyczą wyłącznie stałych.
Prawdopodobnie co ważniejsze, naprawdę nie ma takiej potrzeby. Zamiast tego używaj importu statycznego i wyraźnie mów o tym, co importujesz i skąd.
źródło
interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }
. : Mogę sobie wyobrazić, że jest kilka różnych stałychRGB
,RGBA
,CMYK
,GREYSCALE
,INDEXED
itd.Powiedziałbym, że jeśli jesteś zainteresowany prawidłowym użyciem nowej funkcji, spójrz na kod w JDK. Domyślne metody interfejsów są bardzo szeroko stosowane i łatwe do znalezienia, ale metody statyczne na interfejsach wydają się bardzo rzadkie. Niektóre przykłady obejmują
java.time.chrono.Chronology
,java.util.Comparator
,java.util.Map
, i całkiem kilka interfejsów wjava.util.function
ijava.util.stream
.Większość przykładów, na które patrzyłem, obejmuje użycie tych interfejsów API, które prawdopodobnie będą bardzo powszechne: konwersja między różnymi typami w interfejsie API czasu, na przykład w celu porównań, które prawdopodobnie będą często wykonywane między obiektami przekazywanymi do funkcji lub obiektami zawartymi w kolekcje. Moja na wynos: jeśli istnieje część interfejsu API, która musi być elastyczna, ale możesz na przykład powierzyć 80% przypadków użycia kilkoma ustawieniami domyślnymi, rozważ użycie metod statycznych na interfejsach. Biorąc pod uwagę, jak rzadko ta funkcja wydaje się być używana w JDK, byłbym bardzo konserwatywny, kiedy korzystałbym z niej we własnym kodzie.
Twoje przykłady nie przypominają tego, co widzę w JDK. W tych konkretnych przypadkach prawie na pewno powinieneś stworzyć taki,
enum
który implementuje pożądany interfejs. Interfejs opisuje zestaw zachowań i tak naprawdę nie ma biznesu utrzymującego kolekcję jego implementacji.źródło
A może kompatybilność ze starszymi maszynami JVM?
Nie. Jest to zmiana niezgodna wstecz, która dodaje Zilch do ekspresji języka. Dwa kciuki w dół.
Zdecydowanie zalecam używanie klas do przechowywania szczegółów implementacji. Interfejs naprawdę ma po prostu powiedzieć „ten obiekt obsługuje operacje XYZ” i nie ma to nic wspólnego z żadnymi statycznymi metodami, które możesz pomyśleć o umieszczeniu deklaracji interfejsu. Ta funkcja przypomina rozkładany fotel z wbudowaną mini lodówką. Pewnie, że ma pewne zastosowania niszowe, ale nie przyciąga wystarczająco dużo wagi, aby zasługiwać na mainstream.
AKTUALIZACJA: Proszę, nie więcej głosów negatywnych. Najwyraźniej niektórzy myślą, że statycznymi metodami w interfejsach są kolana pszczoły i niniejszym kapituluję. Tak szybko usunę tę odpowiedź, ale dołączone do niej komentarze są interesującą dyskusją.
źródło