Ostatnie argumenty w metodach interfejsu - o co chodzi?

189

W Javie jest całkowicie legalne definiowanie finalargumentów w metodach interfejsu i nie przestrzeganie tego w klasie implementującej, np .:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

W powyższym przykładzie, bari bazma przeciwległe finaldefinicji w klasie VS interfejsu.

W ten sam sposób nie finalsą wymuszane żadne ograniczenia, gdy jedna metoda klasy rozszerza inną, albo abstractnie.

Chociaż finalma jakąś praktyczną wartość w treści metody klasy, czy jest jakiś punkt określający finalparametry metody interfejsu?

mindas
źródło
finali tak nie robi nic z rodzimymi typami, ponieważ są one kopiowane.
Paul Tomblin
11
W ramach dyskusji: właśnie go wypróbowałem, a jeśli dwie interfacedefinicje różnią się tylko finalatrybutem argumentu, wówczas .classpliki wynikowe są identyczne bajt po bajcie (i oczywiście javap -vdaje to samo wyjście). To samo dotyczy dwóch klas, które final, nawiasem mówiąc, różnią się tylko atrybutem!
Joachim Sauer
2
@Paul: robi dokładnie to samo, co w przypadku typów referencyjnych: zapobiega modyfikacji samych argumentów (jeśli są używane w implementacji).
Joachim Sauer
Ma to tyle samo znaczenia, co opinia publiczna w sygnaturze metody.
Robin
2
@Deepak: Widzę, że pytasz o praktyczne przykłady na wszelkiego rodzaju pytania, nawet jeśli nie ma to większego sensu. Myślę, że powinieneś spróbować nauczyć się abstrakcyjnego myślenia: spróbuj pomyśleć o problemie bez posiadania przed sobą kodu wykonywalnego. Na dłuższą metę bardzo ci to pomoże.
Joachim Sauer

Odpowiedzi:

104

Nie wydaje się, żeby to miało jakiś sens. Zgodnie ze specyfikacją języka Java 4.12.4 :

Deklaracja zmiennej końcowej może służyć jako przydatna dokumentacja, której wartość się nie zmieni i może pomóc uniknąć błędów programistycznych.

Jednak finalmodyfikator parametru metody nie jest wspomniany w regułach dopasowywania podpisów zastąpionych metod i nie ma wpływu na obiekt wywołujący, tylko w treści implementacji. Ponadto, jak zauważył Robin w komentarzu, finalmodyfikator parametru metody nie ma wpływu na wygenerowany kod bajtowy. (Nie dotyczy to innych zastosowań final.)

Ted Hopp
źródło
Czy parametr metody również kwalifikuje się jako zmienna? To oczywiście jest w praktyce, ale czy w kontekście specyfikacji?
mindas
11
Nie można go użyć do dopasowania podpisów, ponieważ nie pojawia się w rzeczywistym pliku .class. To jest tylko dla kompilatora.
Robin
@indas - JLS mówi, że istnieje siedem rodzajów zmiennych . Parametry metody są czwarte na liście.
Ted Hopp
3
Dokumentacja jest jednak prawie bezużyteczna, ponieważ finalmodyfikator nie jest egzekwowany w klasie implementacyjnej. Twój podpis interfejsu może po prostu kłamać.
Stefan Haberl,
Specyfikacja języka Java 8 mówi teraz, że istnieje osiem rodzajów zmiennych (w porównaniu z siedmioma - dodały parametry lambda ). Parametry metody są nadal czwarte na liście (przynajmniej niektóre rzeczy w życiu wydają się stabilne. :-)).
Ted Hopp
25

Niektóre IDE skopiują podpis metody abstrakcyjnej / interfejsu podczas wstawiania metody implementującej do podklasy.

Nie sądzę, żeby miało to jakiekolwiek znaczenie dla kompilatora.

EDYCJA: Chociaż uważam, że tak było w przeszłości, nie sądzę, aby obecne IDE to robiły.

Peter Lawrey
źródło
2
Ważny punkt, chociaż nie sądzę, aby było wiele IDE, kiedy ta funkcja została zaimplementowana (lub przypadkowo odeszła) :-)
mindas
2
Myślę, że należy do kategorii static transientpól. ;)
Peter Lawrey
1
I publicznych konstruktorów na abstrakcyjnej klasie.
Peter Lawrey
1
IntelliJ to robi. Moja wersja to IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Dormouse
1
@ Domku, to interesujące, ponieważ Android Studio (3.1.4 kompilacja # AI-173.4907809) nie :(
Ojciec Chrzestny
18

Końcowe adnotacje parametrów metody są zawsze istotne tylko dla implementacji metody, nigdy dla osoby wywołującej. Dlatego nie ma rzeczywistego powodu, aby używać ich w podpisach metod interfejsu. Chyba że chcesz stosować ten sam spójny standard kodowania, który wymaga ostatecznych parametrów metody, we wszystkich sygnaturach metod. Miło jest móc to zrobić.

jmg
źródło
6

Aktualizacja: Oryginalna odpowiedź poniżej została napisana bez pełnego zrozumienia pytania, dlatego też nie odnosi się bezpośrednio do pytania. :)Niemniej jednak musi być pouczająca dla osób, które chcą zrozumieć ogólne użycie finalsłowa kluczowego.

Jeśli chodzi o pytanie, chciałbym zacytować swój komentarz od dołu.

Uważam, że nie jesteś zmuszony wdrożyć ostateczności argumentu, abyś mógł zdecydować, czy powinien on być ostateczny, czy nie we własnej implementacji.

Ale tak, to brzmi dość dziwnie, że można zadeklarować to finalw interfejsie, ale nie jest to ostateczne wdrożenie. Byłoby bardziej sensowne, gdyby:

za. finalsłowo kluczowe nie było dozwolone dla argumentów metody interfejsu (abstrakcyjnej) (ale można go użyć w implementacji), lub
b. zadeklarowanie argumentu jako finalinterfejsu zmusiłoby go do zadeklarowania go finalw implementacji (ale nie do wymuszenia w przypadku niefinałów).


Mogę wymyślić dwa powody, dla których podpis metody może mieć finalparametry: Beans i Objects (w rzeczywistości oba są z tego samego powodu, ale nieco inne konteksty ).

Obiekty:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

finalKluczowe zapewnić, że nie będzie przypadkowo utworzyć nową lokalną garnek do gotowania, pokazując błąd kompilacji, gdy staraliśmy się zrobić. Dzięki temu bulion z kurczaka został dodany do naszego oryginalnego garnka do gotowania, który addChickenotrzymaliśmy według metody. Porównaj to z addVegetablesmiejscem, w którym straciliśmy kalafior, ponieważ dodało to do nowego lokalnego garnka do gotowania zamiast oryginalnego garnka, który dostał.

Fasola: Jest to ta sama koncepcja co obiekty (jak pokazano powyżej) . Ziarna są zasadniczo Objectw Javie. Jednak fasole (JavaBeans) są używane w różnych aplikacjach jako wygodny sposób przechowywania i przekazywania określonej kolekcji powiązanych danych. Podobnie jak addVegetablesmoże to zepsuć proces gotowania, tworząc nowy garnek do gotowania StringBuilderi wyrzucając go z kalafiorem, tak samo może zrobić to samo z garnkiem JavaBean .

ADTC
źródło
Dobrze. To naprawdę nie jest poprawna odpowiedź na pytanie (ponieważ nie jestem zmuszony, aby moja implementacja metody interfejsu przyjmowała ostateczne argumenty, nawet jeśli interfejs tak mówi), ale nadal jest pomocna, ponieważ jest dobrym wyjaśnieniem, dlaczego na początek warto zadbać o to, aby parametry metody były ostateczne.
Phil
Uważam, że nie jesteś zmuszony wdrożyć ostateczności argumentu, abyś mógł zdecydować, czy powinien on być ostateczny, czy nie we własnej implementacji. Ale tak, to brzmi dość dziwnie, że można zadeklarować to finalw interfejsie, ale nie jest to ostateczne wdrożenie. Byłoby bardziej sensowne, gdyby którekolwiek (a) final słowo kluczowe nie było dozwolone dla argumentów metody interfejsu (abstrakcyjne) (ale można go użyć w implementacji), lub (b) zadeklarowanie argumentu, ponieważ finalw interfejsie zmusiłoby go do zadeklarowania finalw implementacji (ale nie zmuszony do niefinałów).
ADTC
„Naprawdę nie jest to prawidłowa odpowiedź na pytanie” Masz rację, nie wiem, o czym myślałem ... to musiały być wczesne wczesne godziny lipca czy coś w tym rodzaju. :)
ADTC
2

Uważam, że może to być zbyteczny szczegół, ponieważ to, czy jest ostateczny, czy nie, jest szczegółem implementacji.

(Coś w rodzaju deklarowania metod / członków w interfejsie jako publicznych).

Piotr
źródło