W C ++ argument odwołania do funkcji pozwala funkcji na wywołanie odwołania do czegoś innego:
int replacement = 23;
void changeNumberReference(int& reference) {
reference = replacement;
}
int main() {
int i = 1;
std::cout << "i=" << i << "\n"; // i = 1;
changeNumberReference(i);
std::cout << "i=" << i << "\n"; // i = 23;
}
Analogicznie, stały argument odwołania do funkcji wyrzuci błąd czasu kompilacji, jeśli spróbujemy zmienić odwołanie:
void changeNumberReference(const int& reference) {
reference = replacement; // compile-time error: assignment of read-only reference 'reference'
}
Teraz, w Javie, doktorzy mówią, że argumenty funkcji typów innych niż pierwotne są referencjami. Przykład z oficjalnych dokumentów:
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move origin of circle to x+deltaX, y+deltaY
circle.setX(circle.getX() + deltaX);
circle.setY(circle.getY() + deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
Następnie okrągowi przypisywane jest odwołanie do nowego obiektu Koło za pomocą x = y = 0. To ponowne przypisanie nie ma jednak trwałości, ponieważ odwołanie zostało przekazane przez wartość i nie może się zmienić.
Dla mnie to wcale nie wygląda jak odwołania do C ++. Nie przypomina zwykłych referencji w C ++, ponieważ nie można sprawić, by odnosiły się do czegoś innego, i nie przypomina referencji w stałej C ++, ponieważ w Javie kod, który zmieniłby (ale tak naprawdę nie) referencję nie rzuca kompilacji błąd czasu.
To zachowanie jest bardziej podobne do wskaźników C ++. Możesz go użyć do zmiany wartości wskazanych obiektów, ale nie możesz zmienić samej wartości wskaźnika w funkcji. Ponadto, podobnie jak w przypadku wskaźników C ++ (ale nie w przypadku odniesień do C ++), w Javie można przekazać „null” jako wartość takiego argumentu.
Moje pytanie brzmi: dlaczego Java używa pojęcia „referencja”? Czy należy rozumieć, że nie przypominają one referencji do C ++? A może rzeczywiście przypominają odniesienia do C ++ i czegoś mi brakuje?
Odpowiedzi:
Czemu? Ponieważ chociaż spójna terminologia jest ogólnie dobra dla całego zawodu, projektanci języków nie zawsze szanują użycie języka przez innych projektantów języków, szczególnie jeśli te inne języki są postrzegane jako konkurenci.
Ale tak naprawdę żadne użycie „odniesienia” nie było bardzo dobrym wyborem. „Odwołania” w C ++ są po prostu konstrukcją językową do jawnego wprowadzania aliasów (alternatywnych nazw dla dokładnie tego samego bytu). Sprawa byłaby o wiele wyraźniejsza, ponieważ po prostu nazwali nową funkcję „aliasami”. Jednak w tym czasie dużą trudnością było sprawienie, aby wszyscy zrozumieli różnicę między wskaźnikami (które wymagają dereferencji) a referencjami (które tego nie robią), więc ważne było to, że nazywano to czymś innym niż „wskaźnik”, a nie tak konkretnie jakiego terminu użyć.
Java nie ma wskaźników i jest z tego dumna, więc użycie „wskaźnika” jako terminu nie było opcją. Jednak „referencje”, które ma, zachowują się trochę tak, jak wskaźniki C ++, kiedy je przekazujesz - duża różnica polega na tym, że nie możesz wykonywać na nich przykrych operacji na niskim poziomie (rzutowanie, dodawanie ...) , ale skutkują dokładnie taką samą semantyką, gdy przekazujesz uchwyty jednostkom identycznym vs. jednostkom, które po prostu są równe. Niestety termin „wskaźnik” zawiera tak wiele negatywnych skojarzeń niskiego poziomu, że jest mało prawdopodobne, aby społeczność Java go zaakceptowała.
W rezultacie oba języki używają tego samego niejasnego terminu dla dwóch raczej różnych rzeczy, z których obie mogą zyskać na bardziej konkretnej nazwie, ale żadna z nich prawdopodobnie nie zostanie zastąpiona w najbliższym czasie. Język naturalny może być czasem frustrujący!
źródło
NullPointerException
z faktem, że JLS używa terminu „wskaźnik”?NullPointerException
nie jest wskaźnikiem, ale jest wyjątkiem wskazującym, że na niższym poziomie wskaźnik NULL został przekazany / wyrejestrowany. UżywaniePointer
jako część wyjątku, jeśli w ogóle, dodaje do nieprzyjemnego obrazu, jaki mają. JLS musi wspomnieć o wskaźnikach, ponieważ maszyna wirtualna Java została napisana głównie w języku, który ich używa. Nie można dokładnie opisać specyfikacji języka bez opisania, jak działają elementy wewnętrzneOdwołanie to rzecz, która odnosi się do innej rzeczy. Różne języki przypisały bardziej szczegółowe znaczenie słowu „odniesienie”, zwykle „coś w rodzaju wskaźnika bez wszystkich złych aspektów”. C ++ przypisuje określone znaczenie, podobnie jak Java lub Perl.
W C ++ odwołania są bardziej podobne do aliasów (które można zaimplementować za pomocą wskaźnika). Umożliwia to przekazywanie argumentów referencyjnych lub wyjściowych .
W Javie referencje są wskaźnikami, z tym wyjątkiem, że nie jest to poprawiona koncepcja języka: wszystkie obiekty są referencjami, pierwotne typy, takie jak liczby, nie są. Nie chcą mówić „wskaźnik”, ponieważ nie ma arytmetyki wskaźnika i nie ma zweryfikowanych wskaźników w Javie, ale chcą wyjaśnić, że kiedy przekazujesz
Object
argument jako argument, obiekt nie jest kopiowany . To również nie jest przekazywanie przez odniesienie , ale coś więcej jak przekazywanie przez udostępnianie .źródło
W dużej mierze wraca do Algolu 68, a częściowo do reakcji na sposób, w jaki C definiuje wskaźniki.
Algol 68 zdefiniował pojęcie zwane odniesieniem. To było prawie tak samo jak (na przykład) wskaźnik w Pascalu. Była to komórka zawierająca NIL lub adres innej komórki określonego typu. Możesz przypisać odwołanie, aby odwołanie mogło odnosić się do jednej komórki na raz i innej komórki po ponownym przypisaniu. To nie nie , jednak wsparcie cokolwiek analogiczne do C lub C ++ wskaźnik arytmetycznych.
Przynajmniej tak, jak zdefiniował Algol 68, odniesienia były jednak dość czystą koncepcją, która była dość niezdarna w praktyce. Większość definicji zmiennych faktycznie zawierała odniesienia, więc zdefiniowały one skrót, aby nie wymknął się spod kontroli, ale użycie trywialne może i tak stać się dość niezdarne.
Na przykład deklaracja podobna
INT j := 7;
była naprawdę traktowana przez kompilator jako deklaracja podobnaREF INT j = NEW LOC INT := 7
. Tak więc to, co zadeklarowałeś w Algolu 68, było zwykle odwołaniem, które zostało następnie zainicjowane, aby odwoływało się do czegoś, co zostało przydzielone na stercie, i które (opcjonalnie) zostało zainicjowane, aby zawierało pewną określoną wartość. Jednak w przeciwieństwie do Javy starali się przynajmniej zachować rozsądną składnię, zamiast ciągle mieć rzeczy takie jakfoo bar = new foo();
lub próbować powiedzieć fibs o „nasze wskaźniki nie są wskaźnikami”.Pascal i większość jego potomków (zarówno bezpośrednich, jak i ... duchowych) zmieniła nazwę pojęcia referencyjnego Algol 68 na „wskaźnik”, ale sama koncepcja pozostała zasadniczo taka sama: wskaźnik był zmienną, która zawierała albo
nil
adres czegoś, który przydzieliłeś na stercie (tzn. przynajmniej tak jak pierwotnie zdefiniowali Jensen i Wirth, nie było operatora „adres”, więc wskaźnik nie mógł odwoływać się do normalnie zdefiniowanej zmiennej). Pomimo tego, że są „wskaźnikami”, nie była obsługiwana żadna arytmetyka wskaźników.C i C ++ dodały do tego kilka zwrotów akcji. Po pierwsze, zrobić mieć adres-operatora, a więc wskaźnik może odnosić się nie tylko do czegoś przydzielonej na stercie, ale do każdej zmiennej, niezależnie od tego, jak jest przydzielona. Po drugie, definiują arytmetykę wskaźników i definiują indeksy tablic jako po prostu skrótowy zapis arytmetyki wskaźnika, więc arytmetyka wskaźnika jest wszechobecna (obok nieuniknionego) w większości C i C ++.
Kiedy wynaleziono Java, Sun najwyraźniej myślał, że „Java nie ma wskaźników” to prostszy, czystszy przekaz marketingowy niż: „prawie wszystko w Javie jest wskaźnikiem, ale te wskaźniki są w większości jak Pascal zamiast C”. Ponieważ zdecydowali, że „wskaźnik” nie jest akceptowalnym terminem, potrzebowali czegoś innego, a zamiast tego pogłębili „referencje”, mimo że ich referencje były subtelnie (aw niektórych przypadkach nie tak subtelnie) różne od referencji Algolu 68.
Chociaż wyszło to nieco inaczej, C ++ utknął z mniej więcej tym samym problemem: słowo „wskaźnik” było już znane i zrozumiałe, więc potrzebowali innego słowa dla tej dodawanej przez siebie rzeczy, która odnosiła się do czegoś innego, ale poza tym była całkiem inna nieco różni się od tego, co ludzie rozumieli jako „wskaźnik”. Tak więc, mimo że wyraźnie różni się również od odniesienia Algol 68, ponownie użyli również terminu „odniesienie”.
źródło
Pojęcie „typu odniesienia” jest ogólne, może wyrażać zarówno wskaźnik, jak i odniesienie (w kategoriach C ++), w przeciwieństwie do „typu wartości”. Referencje Java są naprawdę wskaźniki bez składniowej napowietrznej
->
o*
dereferencji. Jeśli spojrzysz na implementację JVM w C ++, są to naprawdę proste wskaźniki. Ponadto C # ma pojęcie referencji podobnych do Javy, ale ma również wskaźniki i kwalifikatory „ref” parametrów funkcji, pozwalając na przekazywanie typów wartości przez referencje i unikanie kopiowania, tak jak&
w C ++.źródło