Czy mogę przekazywać parametry przez odniesienie w Javie?

Odpowiedzi:

225

Java jest myląca, ponieważ wszystko jest przekazywane według wartości . Jednak w przypadku parametru typu referencyjnego (tj. Nie jest to parametr typu pierwotnego) jest to samo odniesienie, które jest przekazywane przez wartość, stąd wydaje się, że jest to element przekazujący (a ludzie często twierdzą, że tak jest). Tak nie jest, jak pokazano poniżej:

Object o = "Hello";
mutate(o)
System.out.println(o);

private void mutate(Object o) { o = "Goodbye"; } //NOT THE SAME o!

Wydrukuje Hellona konsoli. Opcje, jeśli chcesz, aby powyższy kod został wydrukowany, Goodbyeto użycie jawnego odniesienia w następujący sposób:

AtomicReference<Object> ref = new AtomicReference<Object>("Hello");
mutate(ref);
System.out.println(ref.get()); //Goodbye!

private void mutate(AtomicReference<Object> ref) { ref.set("Goodbye"); }
oxbow_lakes
źródło
48
Ponadto tablica o długości 1 może być użyta do utworzenia odniesienia, jeśli naprawdę chcesz zmylić ludzi :)
Christoffer
2
AtomicReference i odpowiadające im klasy (AtomicInteger) są najlepsze do takich operacji
Naveen
3
AtomicReference to przesada, niekoniecznie chcesz mieć tam barierę pamięci, a może cię to kosztować. JRuby miał problem z wydajnością z powodu niestabilnej zmiennej używanej podczas konstruowania obiektu. Opcja używania tablicy jako odniesienia ad-hoc wydaje mi się wystarczająco dobra i widziałem, że była używana więcej niż raz.
Elazar Leibovich
Nie sądzę, żeby to było mylące, jednak jest to normalne zachowanie. Typy wartości istnieją na stosie, a typy referencyjne na stercie, ale odniesienia do typów referencyjnych również istnieją na stosie. W ten sposób zawsze operujesz wartością znajdującą się na stosie. Uważam, że pytanie brzmiało: czy można przekazać referencję ze stosu, która wskazuje na inną wartość, która również znajduje się na stosie. Albo podać odniesienie do innego odniesienia, które wskazuje na jakąś wartość, która żyje na kupie?
eomeroff
to mam świetne informacje. Używałem tablic, ale wygląda na to, że jest to coś stworzonego do tego zadania. nie wiem jak to
wygląda w
65

Czy mogę przekazywać parametry przez odniesienie w Javie?

Nie.

Czemu ? Java ma tylko jeden sposób przekazywania argumentów do metod: według wartości.

Uwaga:

W przypadku prymitywów jest to łatwe do zrozumienia: otrzymujesz kopię wartości.

W przypadku wszystkich innych otrzymujesz kopię referencji i nazywa się to również przekazywaniem wartości.

To wszystko jest na tym obrazku:

wprowadź opis obrazu tutaj

Peter Mmm
źródło
25

W Javie nie ma nic na poziomie językowym podobnym do ref . W Javie istnieje tylko przekazywanie wartości semantycznej

Z ciekawości możesz zaimplementować semantykę podobną do ref w Javie, po prostu opakowując swoje obiekty w zmienną klasę:

public class Ref<T> {

    private T value;

    public Ref(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

    public void set(T anotherValue) {
        value = anotherValue;
    }

    @Override
    public String toString() {
        return value.toString();
    }

    @Override
    public boolean equals(Object obj) {
        return value.equals(obj);
    }

    @Override
    public int hashCode() {
        return value.hashCode();
    }
}

przypadek testowy:

public void changeRef(Ref<String> ref) {
    ref.set("bbb");
}

// ...
Ref<String> ref = new Ref<String>("aaa");
changeRef(ref);
System.out.println(ref); // prints "bbb"
dfa
źródło
Dlaczego nie użyć zamiast tego java.lang.ref.Reference?
Kodowane na stałe
java.lang.ref.Reference nie ma ustawionej metody, nie można jej zmieniać
dfa
dlaczego nie użyć AtomicReference(dla nieprymitywów)? Lub AtomicSomething(np . AtomicBoolean:) dla prymitywów?
Arvin
Niestety <T> nie akceptuje typów pierwotnych. Czy chcesz to zrobić: Ref <int>
jk7
1
@ jk7 Czy naprawdę ważne jest, aby używać intzamiast Integer, boolzamiast Booleani tak dalej? Oznacza to, że klasy opakowania (które są automatycznie rozpakowywane, jeśli martwisz się o składnię) nie działają?
Paul Stelian
18

Od Jamesa Goslinga w „The Java Programming Language”:

„... W Javie istnieje dokładnie jeden tryb przekazywania parametrów - przekazywanie przez wartość - dzięki czemu wszystko jest proste. ..”

duffymo
źródło
2
Wypowiedź boga języka należy uznać za odpowiedź.
ingyhere
3
@ingyhere:: The pronouncement of the language god should be declared the answerNiezupełnie. Wbrew temu, co myśli lub wierzy twórca Javy, absolutną odpowiedzią byłby cytat ze specyfikacji języka.
paercebal
1
W rzeczywistości jest w JLS, w sekcji 8.4.1 , a JLS cytuje Goslinga jako pierwszego autora: „Kiedy wywoływana jest metoda lub konstruktor (§15.12), wartości rzeczywistych wyrażeń argumentów inicjują nowo utworzone zmienne parametrów , każda zadeklarowanego typu, przed wykonaniem treści metody lub konstruktora. "
ingyhere
Dokładnie. Sarkazm od członków o niskiej reputacji nie jest pomocny.
duffymo
11

Myślę, że nie możesz. Najlepszą opcją może być hermetyzacja rzeczy, którą chcesz przekazać „przez ref”, do innej instancji klasy i przekazanie (zewnętrznej) referencji klasy (według wartości). Jeśli widzisz co mam na myśli...

tzn. Twoja metoda zmienia stan wewnętrzny przekazywanego obiektu, który jest następnie widoczny dla wywołującego.

Marc Gravell
źródło
7

Java jest zawsze przekazywana przez wartość.

Kiedy przekazujesz prymityw, jest to kopia wartości, kiedy przekazujesz obiekt, jest to kopia wskaźnika referencyjnego.

Nick Holt
źródło
4

Inną opcją jest użycie tablicy, np. void method(SomeClass[] v) { v[0] = ...; } Ale 1) tablica musi być zainicjalizowana przed wywołaniem metody, 2) nadal nie można w ten sposób zaimplementować np. Metody swap ... Tak jest w JDK, np java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[]). W.

Michał Misiak
źródło