Java มีคำหลักอ้างอิงและออกของ C # หรือไม่?


113

สิ่งต่อไปนี้:

ตัวอย่างอ้างอิง:

void changeString(ref String str) {
    str = "def";
}

void main() {
    String abc = "abc";
    changeString(ref abc);
    System.out.println(abc); //prints "def"
}

ออกตัวอย่าง:

void changeString(out String str) {
    str = "def";
}

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}


5
IMO คุณไม่พลาดมาก เวลาเท่านั้นที่ฉันเคยใช้refหรือoutใน C # คือเมื่อฉันกำลังใช้รูปแบบเหมือนTryParse()ที่วิธีการส่งกลับผลบูลีนและวิธีเดียวที่จะได้รับค่าแจงออกมาจากมันคือการใช้หรือref out
Robert Harvey

3
เดาสินั่นคือสิ่งที่ฉันต้องใช้! ;)
กินเอลิเซียม

วิธีอื่นในการทำเช่นนี้คือการส่งคืนวัตถุผสมที่มีทั้งสถานะและค่าที่เป็นโมฆะอยู่ในนั้น แต่ฉันยอมรับว่ามันเป็นบิต Rube Goldberg-ish
Robert Harvey

1
ไม่มีอะไรผิดปกติกับการส่งคืนอ็อบเจ็กต์คอมโพสิตหากมีเพียงอ็อบเจ็กต์ที่ใช้งานได้ที่กำหนดไว้ล่วงหน้า (เช่นทูเปิล) แต่เดี๋ยวก่อนต้องใช้ generics ที่ไม่ถูกลบซึ่งทำงานร่วมกับประเภทดั้งเดิมเพื่อให้มีประสิทธิภาพ :)
Pavel Minaev

คำตอบ:


102

ไม่ Java ไม่มีบางอย่างเช่น C # refและoutคีย์เวิร์ดสำหรับส่งต่อโดยอ้างอิง

คุณสามารถส่งผ่านค่าใน Java เท่านั้น แม้แต่การอ้างอิงก็ส่งผ่านค่า ดูหน้าของJon Skeetเกี่ยวกับการส่งผ่านพารามิเตอร์ใน Javaสำหรับรายละเอียดเพิ่มเติม

หากต้องการทำสิ่งที่คล้ายกับrefหรือoutคุณจะต้องห่อพารามิเตอร์ของคุณไว้ในออบเจ็กต์อื่นและส่งการอ้างอิงอ็อบเจ็กต์นั้นเป็นพารามิเตอร์


5
สิ่งนี้ควรขยายในบางส่วน คุณสามารถส่งผ่าน primitives เท่านั้น (int, short, char ฯลฯ ) เป็นค่า และไม่มีไม่มีออก
Corey Sunwold

14
นั่นไม่เป็นความจริง 100% หากคุณส่งอาร์เรย์หรือคลาสการอ้างอิงไปยังอาร์เรย์หรืออ็อบเจ็กต์จะถูกส่งด้วยค่าคุณสามารถเปลี่ยนอินเทอร์นัลเป็นอาร์เรย์หรืออ็อบเจ็กต์และจะแสดงในตัวเรียก
Romain Hippeau

12
@fearofawhackplanet: refอืมถ้าคุณใช้
Robert Harvey

2
@fearofawhackplanet: Reference parameters need the ref modifier as part of both the declaration and the invocation - that means it's always clear when you're passing something by reference. yoda.arachsys.com/csharp/parameters.html
Mark Byers

5
จากมุมมองของ CLR คุณกำลังส่งการอ้างอิงที่มีการจัดการ ( T&) ตามค่าใช่ แต่ C # มีคำศัพท์เฉพาะของตัวเองและไม่รวมถึงสิ่งต่างๆเช่นค่าประเภทref T- จากมุมมอง C #refเป็นตัวปรับพารามิเตอร์โดยเคร่งครัดและการพูดถึง "การส่งต่อการอ้างอิงตามค่า" ในแง่ที่ไม่สมเหตุสมผล .
Pavel Minaev

30

คำตอบโดยตรง: ไม่

แต่คุณสามารถจำลองการอ้างอิงกับห่อ

และทำสิ่งต่อไปนี้:

void changeString( _<String> str ) {
    str.s("def");
}

void testRef() {
     _<String> abc = new _<String>("abc");
     changeString( abc );
     out.println( abc ); // prints def
}

ออก

void setString( _<String> ref ) {
    str.s( "def" );
}
void testOut(){
    _<String> abc = _<String>();
    setString( abc );
    out.println(abc); // prints def
}

และโดยทั่วไปประเภทอื่น ๆ เช่น:

_<Integer> one = new <Integer>(1);
addOneTo( one );

out.println( one ); // May print 2

28
อุ๊ยตาย ที่น่าเกลียด
กินเอลิเซียม

46
ฉันไม่เคยพูดว่าคุณสามารถทำได้อย่างสง่างามแบบนี้ : P
OscarRyz

เหตุใดสิ่งต่อไปนี้จึงใช้ไม่ได้: Private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = จำนวนเต็มใหม่ (st.nextToken ()); } แยกวิเคราะห์ (ขึ้นบรรทัดใหม่สตริงใหม่ [] {a} จำนวนเต็มใหม่ [] {b});
Elad Benda

3
@ user311130 รหัสของคุณอ่านยาก แต่คุณสามารถสร้างคำถามใหม่โดยพูดว่า: "ฉันพบคำตอบนี้ <link to my answer> แต่คำตอบต่อไปนี้ใช้ไม่ได้ <your code here>
OscarRyz

นั่นเป็นสิ่งที่น่ารังเกียจ แต่ฉันยังคงให้สิ่งนี้อยู่เพราะมันแก้ปัญหาด้วยวิธีที่ "สะอาดที่สุด" ที่ฉันคิดได้สำหรับ Java ในเวลานี้ ...
Pangamma

8

ที่จริงมีค่าโทษมิได้ออกเทียบเท่าคำหลักในJavaภาษาเท่าที่ผมทราบ อย่างไรก็ตามฉันเพิ่งเปลี่ยนรหัสC #เป็นJavaที่ใช้ออกไปพารามิเตอร์และจะแนะนำสิ่งที่ฉันเพิ่งทำไป คุณควรรวมอ็อบเจกต์ใด ๆ ลงในคลาส wrapper และส่งผ่านค่าที่รวมอยู่ในอินสแตนซ์วัตถุ wrapper ดังนี้

ตัวอย่างง่ายๆสำหรับการใช้ Wrapper

นี่คือคลาส Wrapper ;

public class Wrapper {
    public Object ref1; // use this as ref
    public Object ref2; // use this as out

    public Wrapper(Object ref1) {
        this.ref1 = ref1;
    }
}

และนี่คือรหัสทดสอบ

public class Test {

    public static void main(String[] args) {
        String abc = "abc";
        changeString(abc);
        System.out.println("Initial object: " + abc); //wont print "def"

        Wrapper w = new Wrapper(abc);
        changeStringWithWrapper(w);
        System.out.println("Updated object: " + w.ref1);
        System.out.println("Out     object: " + w.ref2);
    }

    // This won't work
    public static void changeString(String str) {
        str = "def";
    }

    // This will work
    public static void changeStringWithWrapper(Wrapper w) {
        w.ref1 = "def";
        w.ref2 = "And this should be used as out!";
    }

}

ตัวอย่างโลกแห่งความจริง

วิธี AC # .NET โดยใช้พารามิเตอร์ out

ที่นี่มี.NET c #วิธีการที่จะใช้ออกคำหลัก;

public bool Contains(T value)
{
    BinaryTreeNode<T> parent;
    return FindWithParent(value, out parent) != null;
}

private BinaryTreeNode<T> FindWithParent(T value, out BinaryTreeNode<T> parent)
{
    BinaryTreeNode<T> current = _head;
    parent = null;

    while(current != null)
    {
        int result = current.CompareTo(value);

        if (result > 0)
        {
            parent = current;
            current = current.Left;
        }
        else if (result < 0)
        {
            parent = current;
            current = current.Right;
        }
        else
        {
            break;
        }
    }

    return current;
}

Java เทียบเท่าของรหัส C # ที่ใช้พารามิเตอร์ out

และJava ที่เทียบเท่ากับวิธีนี้ด้วยความช่วยเหลือของคลาส wrapperมีดังนี้

public boolean contains(T value) {
    BinaryTreeNodeGeneration<T> result = findWithParent(value);

    return (result != null);
}

private BinaryTreeNodeGeneration<T> findWithParent(T value) {
    BinaryTreeNode<T> current = head;
    BinaryTreeNode<T> parent = null;
    BinaryTreeNodeGeneration<T> resultGeneration = new BinaryTreeNodeGeneration<T>();
    resultGeneration.setParentNode(null);

    while(current != null) {
        int result = current.compareTo(value);

        if(result >0) {
            parent = current;
            current = current.left;
        } else if(result < 0) {
            parent = current;
            current = current.right;
        } else {
            break;
        }
    }

    resultGeneration.setChildNode(current);
    resultGeneration.setParentNode(parent);

    return resultGeneration;
}

คลาส Wrapper

และคลาส wrapper ที่ใช้ในโค้ด Java นี้มีดังต่อไปนี้

public class BinaryTreeNodeGeneration<TNode extends Comparable<TNode>>  {

    private BinaryTreeNode<TNode>   parentNode;
    private BinaryTreeNode<TNode>   childNode;

    public BinaryTreeNodeGeneration() {
        this.parentNode = null;
        this.childNode = null;
    }

    public BinaryTreeNode<TNode> getParentNode() {
        return parentNode;
    }

    public void setParentNode(BinaryTreeNode<TNode> parentNode) {
        this.parentNode = parentNode;
    }

    public BinaryTreeNode<TNode> getChildNode() {
        return childNode;
    }

    public void setChildNode(BinaryTreeNode<TNode> childNode) {
        this.childNode = childNode;
    }

}

ดูคำตอบเดิม
pashute

การให้คำตอบโดยละเอียดการโหวตเชิงลบเป็นเรื่องตลก ฉันได้ตรวจสอบ SO แล้วและไม่พบคำตอบที่แน่นอนด้วยรหัสสาธิตนั่นคือเหตุผลที่ฉันเขียนคำตอบนี้เท่าที่ฉันจำได้ หากคุณรอคำตอบเดียวและลงคะแนนทุกคำตอบดังนั้นควรห้ามคำตอบทั้งหมดนอกเหนือจากที่ได้รับการยืนยันจากเจ้าของคำถาม
Levent Divilioglu

โอเคฉันจะลบโหวตลบได้ก็ต่อเมื่อคุณแก้ไขคำตอบ ดังนั้นอ่านคำตอบอื่น ๆ และอธิบายว่าเหตุใดคุณจึงไม่ต้องการใช้วิธีแก้ปัญหาเหล่านั้น กระดาษห่อหุ้ม (และโดยเฉพาะอย่างยิ่งกระดาษห่อ REF และ OUT เพื่อความสนุก) 5 คนให้คำตอบสั้น ๆ และเต็มพร้อมตัวอย่างและมีเพียง Eyal เท่านั้นที่เขียนว่า: "ไม่คุณทำไม่ได้"
pashute

1
คำตอบนี้ดูดี คล้ายกับคำตอบด้านบน แต่ให้ตัวอย่างโดยละเอียดเกี่ยวกับวิธีใช้คลาส Wrapper ใน Java
hubatish

8

Java ส่งผ่านพารามิเตอร์ตามค่าและไม่มีกลไกใด ๆ ที่อนุญาตให้ส่งผ่านโดยอ้างอิง นั่นหมายความว่าเมื่อใดก็ตามที่พารามิเตอร์จะผ่านมันคุ้มค่าจะถูกคัดลอกลงในกรอบสแต็คการจัดการการโทร

ค่าเทอมที่ฉันใช้ที่นี่ต้องการคำชี้แจงเล็กน้อย ใน Java เรามีตัวแปรสองประเภท - ดั้งเดิมและวัตถุ ค่าของวัตถุดั้งเดิมคือตัวดั้งเดิมและค่าของวัตถุคือการอ้างอิง (ไม่ใช่สถานะของวัตถุที่อ้างอิง) ดังนั้นการเปลี่ยนแปลงค่าใด ๆ ภายในเมธอดจะเปลี่ยนเฉพาะสำเนาของค่าในสแตกเท่านั้นและผู้เรียกจะไม่เห็น ตัวอย่างเช่นไม่มีวิธีใดในการใช้วิธีการแลกเปลี่ยนจริงที่ได้รับการอ้างอิงสองรายการและแลกเปลี่ยนข้อมูลเหล่านั้น (ไม่ใช่เนื้อหา!)


6

เช่นเดียวกับคนอื่น ๆ ฉันต้องแปลงโปรเจ็กต์ C # เป็น Java ฉันไม่พบวิธีแก้ปัญหาที่สมบูรณ์บนเว็บเกี่ยวกับตัวปรับแต่งออกและอ้างอิง แต่ฉันสามารถนำข้อมูลที่พบมาขยายผลเพื่อสร้างชั้นเรียนของตัวเองเพื่อให้เป็นไปตามข้อกำหนด ฉันต้องการสร้างความแตกต่างระหว่างrefและoutพารามิเตอร์เพื่อความชัดเจนของโค้ด ด้วยชั้นเรียนด้านล่างนี้เป็นไปได้ ขอให้ข้อมูลนี้ช่วยประหยัดเวลาและความพยายามของผู้อื่น

ตัวอย่างรวมอยู่ในโค้ดด้านล่าง

//*******************************************************************************************
//XOUT CLASS
//*******************************************************************************************
public class XOUT<T>
{
    public XOBJ<T> Obj = null;

    public XOUT(T value)
    {
        Obj = new XOBJ<T>(value);
    }

    public XOUT()
    {
      Obj = new XOBJ<T>();
    }

    public XOUT<T> Out()
    {
        return(this);
    }

    public XREF<T> Ref()
    {
        return(Obj.Ref());
    }
};

//*******************************************************************************************
//XREF CLASS
//*******************************************************************************************

public class XREF<T>
{
    public XOBJ<T> Obj = null;

    public XREF(T value)
    {
        Obj = new XOBJ<T>(value);
    }

    public XREF()
    {
      Obj = new XOBJ<T>();
    }

    public XOUT<T> Out()
    {
        return(Obj.Out());
    }

    public XREF<T> Ref()
    {
        return(this);
    }
};

//*******************************************************************************************
//XOBJ CLASS
//*******************************************************************************************
/**
 *
 * @author jsimms
 */
/*
    XOBJ is the base object that houses the value. XREF and XOUT are classes that
    internally use XOBJ. The classes XOBJ, XREF, and XOUT have methods that allow
    the object to be used as XREF or XOUT parameter; This is important, because
    objects of these types are interchangeable.

    See Method:
       XXX.Ref()
       XXX.Out()

    The below example shows how to use XOBJ, XREF, and XOUT;
    //
    // Reference parameter example
    //
    void AddToTotal(int a, XREF<Integer> Total)
    {
       Total.Obj.Value += a;
    }

    //
    // out parameter example
    //
    void Add(int a, int b, XOUT<Integer> ParmOut)
    {
       ParmOut.Obj.Value = a+b;
    }

    //
    // XOBJ example
    //
    int XObjTest()
    {
       XOBJ<Integer> Total = new XOBJ<>(0);
       Add(1, 2, Total.Out());    // Example of using out parameter
       AddToTotal(1,Total.Ref()); // Example of using ref parameter
       return(Total.Value);
    }
*/


public class XOBJ<T> {

    public T Value;

    public  XOBJ() {

    }

    public XOBJ(T value) {
        this.Value = value;
    }

    //
    // Method: Ref()
    // Purpose: returns a Reference Parameter object using the XOBJ value
    //
    public XREF<T> Ref()
    {
        XREF<T> ref = new XREF<T>();
        ref.Obj = this;
        return(ref);
    }

    //
    // Method: Out()
    // Purpose: returns an Out Parameter Object using the XOBJ value
    //
    public XOUT<T> Out()
    {
        XOUT<T> out = new XOUT<T>();
        out.Obj = this;
        return(out);
    }

    //
    // Method get()
    // Purpose: returns the value
    // Note: Because this is combersome to edit in the code,
    // the Value object has been made public
    //
    public T get() {
        return Value;
    }

    //
    // Method get()
    // Purpose: sets the value
    // Note: Because this is combersome to edit in the code,
    // the Value object has been made public
    //
    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();
    }
}


-1

java ไม่มีวิธีมาตรฐานในการทำ การแลกเปลี่ยนส่วนใหญ่จะทำในรายการที่บรรจุในคลาส แต่มีวิธีที่ไม่เป็นทางการในการทำ:

package Example;

import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;



 public class Test{


private static <T> void SetValue(T obj,T value){
    try {
        Field f = obj.getClass().getDeclaredField("value");
        f.setAccessible(true);
        f.set(obj,value);
        } catch (IllegalAccessException | IllegalArgumentException | 
            NoSuchFieldException | SecurityException ex) {
            Logger.getLogger(CautrucjavaCanBan.class.getName()).log(Level.SEVERE, 
       null, ex);
        }
}
private  static  void permutation(Integer a,Integer b){
    Integer tmp = new Integer(a);
    SetValue(a, b);
    SetValue(b, tmp);
}
 private  static  void permutation(String a,String b){
    char[] tmp = a.toCharArray();
    SetValue(a, b.toCharArray());
    SetValue(b, tmp);
}
public static void main(String[] args) {
    {
        Integer d = 9;
        Integer e = 8;
        HoanVi(d, e);
        System.out.println(d+" "+ e);
    }
    {
        String d = "tai nguyen";
        String e = "Thai nguyen";
        permutation(d, e);
        System.out.println(d+" "+ e);
    }
}

}

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