ฉันสามารถส่งพารามิเตอร์โดยอ้างอิงใน Java ได้หรือไม่


คำตอบ:


225

Java เป็นความสับสนเพราะทุกอย่างจะผ่านค่า อย่างไรก็ตามสำหรับพารามิเตอร์ของประเภทการอ้างอิง (เช่นไม่ใช่พารามิเตอร์ของประเภทดั้งเดิม) มันเป็นการอ้างอิงตัวเองซึ่งถูกส่งผ่านตามค่าดังนั้นจึงปรากฏว่าเป็นการส่งต่อโดยอ้างอิง (และผู้คนมักอ้างว่าเป็น) นี่ไม่ใช่กรณีดังแสดงโดยต่อไปนี้:

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

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

จะพิมพ์Helloไปที่คอนโซล ตัวเลือกหากคุณต้องการให้โค้ดด้านบนเพื่อพิมพ์Goodbyeนั้นใช้การอ้างอิงที่ชัดเจนดังต่อไปนี้:

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

private void mutate(AtomicReference<Object> ref) { ref.set("Goodbye"); }

48
นอกจากนี้อาร์เรย์ของความยาว 1 สามารถใช้ในการสร้างการอ้างอิงถ้าคุณอยากไปคนสับสน :)
Christoffer

2
AtomicReference และคลาสที่เกี่ยวข้อง (AtomicInteger) ดีที่สุดสำหรับการดำเนินการดังกล่าว
Naveen

3
AtomicReference เป็น overkill คุณไม่จำเป็นต้องมีแผงกั้นหน่วยความจำที่นั่นและอาจทำให้คุณเสียค่าใช้จ่าย JRuby มีปัญหาด้านประสิทธิภาพเนื่องจากตัวแปรที่ใช้เมื่อสร้างวัตถุ ตัวเลือกในการใช้อาเรย์เป็นการอ้างอิงแบบเฉพาะกิจดูเหมือนจะดีพอสำหรับฉันและฉันเห็นว่ามันใช้มากกว่าหนึ่งครั้ง
Elazar Leibovich

ฉันไม่คิดว่านี่เป็นความสับสน แต่นี่เป็นพฤติกรรมปกติประเภทค่าที่มีอยู่ในกองและประเภทการอ้างอิงอยู่ในกอง แต่การอ้างอิงถึงสำหรับประเภทการอ้างอิงอยู่ในกองยัง ดังนั้นคุณจะทำงานกับค่าที่อยู่ในสแต็ก ฉันเชื่อว่าคำถามนี้คือ: เป็นไปได้ไหมที่จะส่งผ่านการอ้างอิงจากสแต็กที่ชี้ไปยังค่าอื่นที่อยู่ในสแต็ก หรือผ่านการอ้างอิงของการอ้างอิงอื่นซึ่งชี้ไปที่คุณค่าบางอย่างที่มีชีวิตอยู่บนกอง?
eomeroff

ฉันเป็นข้อมูลที่ดี ฉันใช้อาร์เรย์ แต่ดูเหมือนว่าจะมีบางอย่างที่สร้างขึ้นสำหรับงานนี้ ฉันไม่ทราบเกี่ยวกับประสิทธิภาพการทำงานของนี้กับอาร์เรย์อย่างไรก็ตามถ้าอยู่ในลูปที่สำคัญต่อประสิทธิภาพ
สตีฟ

65

ฉันสามารถส่งพารามิเตอร์โดยอ้างอิงใน Java ได้หรือไม่

เลขที่

ทำไม Java มีเพียงหนึ่งโหมดของการส่งผ่านข้อโต้แย้งไปยังวิธีการ: ตามค่า

บันทึก:

สำหรับสิ่งพื้นฐานสิ่งนี้เข้าใจง่าย: คุณได้รับสำเนาของค่า

สำหรับคนอื่น ๆ คุณจะได้รับสำเนาของการอ้างอิงและสิ่งนี้เรียกว่ายังผ่านค่า

มันคือทั้งหมดในภาพนี้:

ป้อนคำอธิบายรูปภาพที่นี่


25

ใน Java มีอะไรในระดับภาษาคล้ายกับโทษ ใน Java มีเพียงความหมายผ่านค่า

เพื่อความอยากรู้อยากเห็นคุณสามารถใช้ความหมายอ้างอิงในภาษาจาวาเพียงห่อวัตถุของคุณในคลาสที่ไม่แน่นอน

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();
    }
}

testcase:

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

// ...
Ref<String> ref = new Ref<String>("aaa");
changeRef(ref);
System.out.println(ref); // prints "bbb"

ทำไมไม่ใช้ java.lang.ref.Reference แทน
Hardcoded

java.lang.ref.Reference ไม่มีเมธอด set มันไม่สามารถเปลี่ยนได้
dfa

ทำไมไม่ใช้AtomicReference(สำหรับผู้ที่ไม่ใช่พื้นฐาน)? หรือAtomicSomething(เช่นAtomicBoolean:) สำหรับดั้งเดิม?
Arvin

น่าเสียดายที่ <T> ไม่ยอมรับประเภทดั้งเดิม ต้องการทำสิ่งนี้: Ref <int>
jk7

1
@ jk7 มันเป็นสิ่งที่สำคัญจริงๆพอที่จะมีการใช้intแทนInteger, boolแทนBooleanและอื่น ๆ ? นั่นคือคลาสของ wrapper (ซึ่งจะถูกคลายโดยอัตโนมัติหากคุณกังวลเกี่ยวกับไวยากรณ์) ไม่ทำงาน?
Paul Stelian

18

จาก James Gosling ใน "ภาษาการเขียนโปรแกรม Java":

"... มีโหมดการผ่านพารามิเตอร์หนึ่งตัวใน Java - ผ่านค่า - และนั่นทำให้สิ่งต่าง ๆ ง่ายขึ้น .. "


2
คำประกาศของภาษาพระเจ้าควรได้รับการประกาศคำตอบ
ingyhere

3
@ingyhere: The pronouncement of the language god should be declared the answerไม่จริงหรอก แม้จะมีสิ่งที่ผู้สร้างของ Java คิดหรือเชื่อคำตอบที่แน่นอนจะมาจากคำพูดจากสเปคภาษา
paercebal

1
ในความเป็นจริงมันมีอยู่ในJLS ในส่วน 8.4.1และ JLS อ้างอิงลูกห่านเป็นผู้เขียนครั้งแรก: "เมื่อวิธีการหรือคอนสตรัคถูกเรียก (§15.12) ค่านิยมของการแสดงออกโต้แย้งที่เกิดขึ้นจริงเริ่มต้นที่สร้างขึ้นใหม่ตัวแปรพารามิเตอร์แต่ละ ชนิดที่ประกาศไว้ก่อนการประมวลผลเนื้อความของเมธอดหรือตัวสร้าง "
ingyhere

เผง Sarcasm จากสมาชิกตัวแทนต่ำไม่เป็นประโยชน์
duffymo

11

ฉันไม่คิดว่าคุณจะทำได้ ตัวเลือกที่ดีที่สุดของคุณอาจจะแค็ปซูลสิ่งที่คุณต้องการผ่าน "โดยอ้างอิง" ไปยังอินสแตนซ์ของคลาสอื่นและส่งผ่านการอ้างอิงของคลาส (ภายนอก) (ตามค่า) ถ้าคุณเห็นสิ่งที่ฉันหมายถึง ...

เช่นวิธีการของคุณเปลี่ยนสถานะภายในของวัตถุที่มีการส่งผ่านซึ่งจะปรากฏแก่ผู้โทร


7

Java ผ่านค่าเสมอ

เมื่อคุณผ่านการดั้งเดิมมันเป็นสำเนาของค่าเมื่อคุณผ่านวัตถุมันเป็นสำเนาของตัวชี้การอ้างอิง


4

อีกตัวเลือกหนึ่งคือการใช้อาร์เรย์เช่น void method(SomeClass[] v) { v[0] = ...; } แต่ 1) อาร์เรย์จะต้องเริ่มต้นก่อนที่จะใช้วิธีการเรียก 2) ยังคงเป็นหนึ่งไม่สามารถใช้วิธีการแลกเปลี่ยนเช่นในทางนี้ ... วิธีนี้จะใช้ใน JDK java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[])เช่นใน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.