Przekazuje 'this' w wywołaniu metody akceptowanej praktyki w Javie


93

Czy przekazywanie bieżącego obiektu w wywołaniu metody jest dobrą / złą / akceptowalną praktyką. Jak w:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

W szczególności, czy linia jest bar.foo(this)akceptowalna?


60
Dlaczego miałoby to być nie do przyjęcia? To jest powszechne.
Denys Séguret

3
Więc ... to 8 tak :) (I tak, utrzymuję to na bieżąco.)
Alex Gittemeier

2
niestatyczna klasa anonimowa automatycznie przekazuje odwołanie do superklasy, więc będzie to akceptowalne. jedyną ostrożnością jest ostrożność w przypadku odwołań cyklicznych.
Mehul Rathod

5
Jest jednak jedno zastrzeżenie: nie powinieneś przekazywać tego w konstruktorze, ponieważ wystawiłoby to twój obiekt w niespójnym stanie. Ludzie zazwyczaj robią to, gdy tworzą wywołania zwrotne (np. ActionListener) jako anonimowe klasy wewnętrzne, a następnie przekazują je do innego obiektu.
Tamas Rev

3
@dystroy, chociaż zgadzam się, że jest akceptowalny, co oznacza, że ​​jest akceptowalny, ponieważ jest powszechny, jest naprawdę złą logiką. Robienie czegoś, ponieważ jest to częste, może wpędzić cię w wiele kłopotów
Carrie Kendall,

Odpowiedzi:


155

Nie ma powodu, aby go nie używać, thisjest to bieżąca instancja i jest całkowicie uzasadnione. W rzeczywistości często nie ma prostego sposobu na pominięcie tego.

Więc użyj go.

Ponieważ ciężko jest przekonać, że jest do przyjęcia bez przykładu (negatywna odpowiedź na takie pytanie zawsze jest łatwiejsza do argumentacji), właśnie otworzyłem jedną z najczęściej spotykanych java.langklas, tę Stringi oczywiście znalazłem przykłady takiego użycia np.

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

Szukaj (thisw dużych „zaakceptowanych” projektach, na pewno go znajdziesz.


6
Doskonała odpowiedź.
maxf130

15
-1, ponieważ nie ma wzmianki o tym, że dwukierunkowe relacje klas są bardziej skomplikowane niż relacje jednokierunkowe. Niezwykle ważne jest, aby oprogramowanie było jak najbardziej przejrzyste. W powyższym konkretnym przykładzie rozsądniej byłoby przenieść metodę foo do klasy Baz, aby uniknąć dwukierunkowego odniesienia między dwiema klasami i połączyć zachowanie i dane.
JW.

36
-1 do odpowiedzi, ponieważ znalazłeś w pytaniu dodatkowy drobny szczegół, który możesz skomentować? Naprawdę?
Denys Séguret

13
@dystroy Tak Nie zgadzam się z twoją frazą otwierającą: „Nie ma powodu, aby go nie używać”.
JW.

4
W praktyce, w prawdziwym kodzie (w przeciwieństwie do uproszczonego przykładu OP), przekazanie thisnie oznacza dodania łącza dwukierunkowego, na przykład z powodu dziedziczenia i interfejsów.
Denys Séguret

165

Nie ma w tym nic złego. To, co NIE jest dobrą praktyką, to robienie tego samego wewnątrz konstruktorów, ponieważ podajesz odniesienie do jeszcze nie w pełni zainicjowanego obiektu.

Jest tu podobny post: Java wycieka to w konstruktorze, gdzie wyjaśniają, dlaczego ta ostatnia jest złą praktyką.


18
+1: dobrze jest zwrócić uwagę na zagrożenie w odniesieniu do „tego” w konstruktorach.
Batszeba,

Niekoniecznie jest to zła praktyka. Na przykład Carkonstruktor może tworzyć Wheelinstancje, Carbez Wheelbyłoby niecałkowicie zainicjowane, podczas gdy Wheelbez odpowiednika Carbyłoby również niecałkowicie zainicjowane. W takim przypadku może być dopuszczalne w konstruktorze Car, aby przejść thisdo konstruktora Wheel. Inną alternatywą byłoby, aby zarówno samochód, jak i koło miały prywatnego konstruktora i używały funkcji fabrycznej, która konstruuje samochód, koło i instaluje koło w samochodzie; ale czy powinna to być metoda statyczna w przypadku samochodu czy metoda statyczna w przypadku koła?
Lie Ryan,

Oczywiście powinieneś stworzyć, CarFactoryWheelInstallerProxyktóry zainstaluje koła za Ciebie.
Kevin,

6
@LieRyan Wheeljest całkowicie podporządkowany Car, a IMO nie powinno w ogóle o tym wiedzieć Car.
Izkata,

3
JEDYNĄ złą rzeczą związaną thisz używaniem z poziomu konstruktora jest to, że thisjest to przekazywane do metody lub kontekstu, z którego jeszcze nie w pełni skonstruowane odwołanie do obiektu jest publikowane niezaufanym lub nieznanym klientom (lub kod klienta, który zakłada, że ​​ma widok na w pełni zbudowany obiekt). thisMoim zdaniem przejście od konstruktora do metody prywatnej pakietu, która wykonuje wspólną inicjalizację, jest nie tylko dopuszczalne, ale i pożądane.
scottb

42

Tak , ale należy uważać na dwie rzeczy

  1. Przekazywanie tego gdy obiekt nie został jeszcze skonstruowany (tj. W jego konstruktorze)
  2. Przekazanie tego do długo żyjącego obiektu, który utrzyma odniesienie przy życiu i zapobiegnie wyrzucaniu tego obiektu do śmieci.

1
Zauważ, że konstruktor w Javie nie jest tak naprawdę konstruktorem, prawdopodobnie bardziej odpowiednie jest wywołanie konstruktora Java „inicjatorem”. W konstruktorze Java obiektowi została faktycznie przydzielona pamięć, to znaczy obiekt już istniał / został zbudowany w konstruktorze.
Lie Ryan,

1
Niekoniecznie, obiekt może mieć pewne zmienne instancji, które nie zostały jeszcze zainicjalizowane, więc obiekt nie jest jeszcze w pełni operacyjny. Zatem w konstruktorze można przekazać to do drugiego obiektu, który mógłby wywołać metodę do obiektu, który nie zainicjował jeszcze wszystkich swoich zmiennych instancji.
Stefanos T.

jednak tak długo, jak drugi obiekt jest świadomy, że przekazany obiekt nie jest zainicjowany i traktuje go jako obiekt nieprzezroczysty lub wywołuje tylko metody, które zostały w takim stanie zadeklarowane jako bezpieczne, nie spowoduje to problemu z przekazaniem thisdo niego. Nie byłoby to możliwe, gdyby obiekt nie został przydzielony.
Lie Ryan,


5

oznacza to aktualny obiekt. To, co robisz, jest poprawne pod względem systemowym, ale nie widzę takiej potrzeby, jeśli wywołujesz metodę w tej samej klasie.


2
W przykładowym kodzie Baz.method () jest metodą instancji, która wywołuje Bar.foo () z instancją Baz jako parametrem. Zatem OP nie wywołuje metody w tej samej klasie.
użytkownik

@ MichaelKjörling Myślę, że Juned mówi, że przenosząc metodę foo () do klasy Baz, nie byłoby potrzeby przechodzenia thismiędzy tymi dwiema klasami. Nie ma więc potrzeby dodawania dodatkowej złożoności.
JW.

4

Przekazywanie bieżącego obiektu w wywołaniu metody jest złą praktyką, jeśli istnieją mniej złożone alternatywy umożliwiające osiągnięcie tego samego zachowania.

Z definicji, powiązanie dwukierunkowe jest tworzone zaraz po thisprzekazaniu z jednego obiektu do drugiego.

Cytując Refaktoryzację Martina Fowlera:

Zmień skojarzenie dwukierunkowe na jednokierunkowe (200)

Dwukierunkowe skojarzenia są przydatne, ale mają swoją cenę. Ceną jest dodatkowa złożoność utrzymania dwukierunkowych połączeń i zapewnienie prawidłowego tworzenia i usuwania obiektów. Skojarzenia dwukierunkowe nie są naturalne dla wielu programistów, dlatego często są źródłem błędów

...

Powinieneś używać skojarzeń dwukierunkowych, kiedy tego potrzebujesz, ale nie wtedy, gdy tego nie robisz. Gdy tylko zobaczysz, że dwukierunkowa asocjacja już nie pociąga za sobą, upuść niepotrzebny koniec.

Tak więc teoretycznie powinniśmy słyszeć dzwonki alarmowe, kiedy stwierdzimy, że musimy spasować thisi naprawdę bardzo starać się wymyślić inne sposoby rozwiązania problemu. Oczywiście są chwile, kiedy w ostateczności warto to zrobić.

Często też konieczne jest tymczasowe uszkodzenie projektu, robiąc „złe praktyki” podczas dłuższej refaktoryzacji kodu w celu ogólnej poprawy. (Jeden krok do tyłu, dwa kroki do przodu).

W praktyce znalazłem mój kod poprawiła się znacznie poprzez unikanie dwukierunkowych linków jak ognia.


Mylisz uproszczony przykład z potrzebą ustanowienia połączenia dwukierunkowego. Podanie tego jako parametru, co powinno być jasne w przypadku wielu przykładów kodu źródłowego java.lang (na przykład tego, który widziałeś w mojej odpowiedzi) nie oznacza, że ​​dodajesz zależność dwukierunkową. Moim zdaniem ta odpowiedź powinna być komentarzem.
Denys Séguret

@dystroy Dziękujemy za dodanie komentarza wyjaśniającego, dlaczego głosowałeś w dół. Zawsze dobrze jest wiedzieć. Zmodyfikuję swoją odpowiedź, aby wyjaśnić, że z definicji skojarzenie dwukierunkowe jest tworzone natychmiast po thisprzekazaniu.
JW.

1
„z definicji skojarzenie dwukierunkowe jest tworzone, gdy tylko zostanie przekazane” . To wyjaśnia, gdzie nie rozumiesz. Spójrz na przykład, który podam. Nie ma łącza dwukierunkowego, ponieważ typ argumentu equalsto Object. Jest to bardzo powszechne: metoda odbierająca definiuje swój argument jako bardziej ogólną klasę lub interfejs. Jednym z powodów, dla których taki wzorzec jest używany w Javie, jest unikanie niepożądanych zależności. Zanim przejdziemy dłużej, proponuję przyjrzeć się wielu przypadkom przekazywania thisjako argumentu w szanowanych bibliotekach Java.
Denys Séguret

Po prostu zgódźmy się nie zgodzić. Moje życie stało się znacznie łatwiejsze, ponieważ unikałem przekazywania thiskodu, jeśli to możliwe. Poleciłbym to innym.
JW.

2
@JW: rozumowanie podane w tej odpowiedzi jest nieistotne. Zawsze złym pomysłem jest zrobienie X, jeśli jest coś innego, co jest prostsze, dla dowolnej wartości X.
Lie Ryan,

4

Tak. możesz go użyć. jest to po prostu powszechne w programowaniu, aby przejść, thisale są zalety i wady korzystania z tego, ale nie jest to niebezpieczne.


Istnieje wiele skutków ubocznych. Dodaje złożoności.
JW.

Jeśli istnieje tak wiele efektów ubocznych, nie moglibyśmy znaleźć ani jednego takiego wskazówki w naszym kodzie źródłowym Java. Zobacz przykład @destroys z kodu źródłowego.
Suresh Atta

2

Aby dodać jeszcze jeden przykład, gdzie podanie thisjest poprawne i zgodne z dobrym projektem: wzorzec odwiedzających . We wzorcu projektowym gościa, metodaaccept(Visitor v) jest zwykle implementowana w sposób, który po prostu wywołuje v.visit(this).


1

Do przyjęcia

Fragment z dokumentów Oracle JAVA:

W ramach metody instancji lub konstruktora jest to odniesienie do bieżącego obiektu - obiektu, którego metoda lub konstruktor jest wywoływana. Możesz odwołać się do dowolnego elementu członkowskiego bieżącego obiektu z poziomu metody wystąpienia lub konstruktora, używając this.

Używanie tego z polem

Najczęstszym powodem używania słowa kluczowego this jest to, że pole jest przesłonięte przez metodę lub parametr konstruktora.


2
„Możesz odwołać się do dowolnego elementu członkowskiego bieżącego obiektu ” - to wydaje się nie odpowiadać na pytanie „czy dopuszczalne jest przekazywanie thisjako parametr? ”.
użytkownik

2
Oznacza to, jak można this.some_variableodwołać się do zmiennej klasy zamiast do zmiennej lokalnej. Nie ma to nic wspólnego z przekazywaniem thisjako parametru.
Jose Salvatierra,

0

Wszystko w Javie jest przekazywane według wartości. Ale obiekty NIGDY nie są przekazywane do metody!
Kiedy java przekazuje obiekt do metody, najpierw tworzy kopię referencji do obiektu, a nie kopię samego obiektu. Stąd jest to doskonale stosowana metoda w Javie. I najczęściej obserwowane użycie.


9
To wygląda na temat.
Denys Séguret

4
„Wszystko w Javie jest przekazywane według wartości”. - to bardzo mylący wstępny komentarz. W rzeczywistości wszystkie obiekty są przekazywane przez odwołanie, a wszystkie typy pierwotne są przekazywane przez wartość. NIGDY nie masz thisodniesienia do typu prymitywnego, dlatego myślę, że twoje „więcej informacji” wprowadza zamieszanie.
Stewart,

1
@Stewart: Natychmiast daje do zrozumienia, że ​​nie ma na myśli kopiowania całych obiektów.
LarsH,

10
@Stewart nie, obiekty nie są przekazywane przez odwołanie, a raczej odniesienia do obiektów są przekazywane przez wartość. To ważne rozróżnienie - przekazanie przez odniesienie oznaczałoby, że jeśli mam lokalną zmienną, która odwołuje się do obiektu i przekażę tę zmienną do innej metody, metoda byłaby w stanie zmienić obiekt, do którego odnosi się moja zmienna, a to na pewno nie jest coś możesz to zrobić w Javie. Metoda może zmienić stan obiektu poprzez własną kopię referencji, ale nie może zmienić mojej kopii referencji, aby wskazywała na coś innego.
Ian Roberts,

2
@Stewart yoda.arachsys.com/csharp/parameters.html to dobry artykuł wyjaśniający różnicę między przekazywaniem przez odwołanie a przekazywaniem odwołań według wartości, w kontekście języka C #, który obsługuje oba.
Ian Roberts,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.