Java 8 zezwala na metody interfejsu statycznego
W Javie 8 interfejsy mogą mieć metody statyczne. Mogą także mieć konkretne metody instancji, ale nie pola instancji.
Są tutaj naprawdę dwa pytania:
- Dlaczego w dawnych czasach interfejsy nie mogły zawierać metod statycznych?
- Dlaczego nie można zastąpić metod statycznych?
Metody statyczne w interfejsach
Nie było silnego technicznego powodu, dla którego interfejsy nie mogły mieć metod statycznych w poprzednich wersjach. Ładnie to podsumowuje plakat z duplikatem pytania. Metody interfejsu statycznego były początkowo uważane za małą zmianę języka, a następnie pojawiła się oficjalna propozycja dodania ich w Javie 7, ale później porzucona z powodu nieprzewidzianych komplikacji.
Wreszcie, Java 8 wprowadziła metody interfejsu statycznego, a także metody instancji z możliwością zastąpienia z domyślną implementacją. Nadal nie mogą mieć pól instancji. Te funkcje są częścią obsługi wyrażeń lambda i możesz przeczytać o nich więcej w części H JSR 335.
Przesłanianie metod statycznych
Odpowiedź na drugie pytanie jest nieco bardziej skomplikowana.
Metody statyczne można rozwiązać w czasie kompilacji. Dynamiczne wysyłanie ma sens na przykład w przypadku metod, w których kompilator nie może określić konkretnego typu obiektu, a zatem nie może rozwiązać metody, która ma zostać wywołana. Jednak wywołanie metody statycznej wymaga klasy, a ponieważ klasa ta jest znana statycznie - w czasie kompilacji - dynamiczne wysyłanie nie jest konieczne.
Aby zrozumieć, co się tutaj dzieje, potrzebne jest trochę tła na temat działania metod instancji. Jestem pewien, że rzeczywista implementacja jest zupełnie inna, ale pozwól mi wyjaśnić moje pojęcie wysyłki metody, które modele dokładnie obserwowały zachowanie.
Udawaj, że każda klasa ma tablicę skrótów, która odwzorowuje sygnatury metod (nazwy i typy parametrów) na rzeczywistą część kodu, aby zaimplementować metodę. Gdy maszyna wirtualna próbuje wywołać metodę w instancji, sprawdza obiekt w swojej klasie i wyszukuje żądany podpis w tabeli klasy. Jeśli zostanie znalezione ciało metody, zostanie ono wywołane. W przeciwnym razie uzyskuje się klasę nadrzędną klasy i tam wyszukiwanie jest powtarzane. Dzieje się tak, dopóki nie zostanie znaleziona metoda lub nie będzie już klas nadrzędnych - w wyniku czego powstanieNoSuchMethodError
.
Jeśli zarówno nadklasa, jak i podklasa mają w swoich tabelach wpis dotyczący tej samej sygnatury metody, najpierw pojawia się wersja podklasy, a wersja nadklasy nigdy nie jest używana - jest to „przesłonięcie”.
Załóżmy teraz, że pomijamy instancję obiektu i zaczynamy od podklasy. Rozdzielczość może przebiegać jak wyżej, dając ci rodzaj „nadpisywalnej” metody statycznej. Rozdzielczość może się jednak zdarzyć w czasie kompilacji, ponieważ kompilator zaczyna od znanej klasy, zamiast czekać na środowisko wykonawcze w celu zapytania do obiektu o nieokreślonym typie dla swojej klasy. Nie ma sensu „zastępować” metody statycznej, ponieważ zawsze można określić klasę, która zawiera żądaną wersję.
„Interfejsy” konstruktora
Oto trochę więcej materiałów na temat ostatniej edycji pytania.
Wygląda na to, że chcesz skutecznie zlecić metodę podobną do konstruktora dla każdej implementacji IXMLizable
. Zapomnij na chwilę o wymuszeniu tego za pomocą interfejsu i udawaj, że masz klasy, które spełniają to wymaganie. Jak byś tego użył?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Ponieważ musisz wyraźnie nazwać konkretny typ Foo
podczas „konstruowania” nowego obiektu, kompilator może sprawdzić, czy rzeczywiście ma on niezbędną metodę fabryczną. A jeśli nie, to co z tego? Jeśli mogę zaimplementować taki, IXMLizable
który nie ma „konstruktora”, i utworzę instancję i przekażę ją do twojego kodu, będzie on miał IXMLizable
niezbędny interfejs.
Budowa jest częścią implementacji, a nie interfejsu. Każdy kod, który działa poprawnie z interfejsem, nie dba o konstruktora. Każdy kod, który dba o konstruktor, musi znać konkretny typ, a interfejs można zignorować.