Czy Java ma coś w rodzaju słów kluczowych ref i out w języku C #?


113

Coś podobnego do następującego:

przykład referencyjny:

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

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

nasz przykład:

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

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


5
IMO nie tracisz wiele. Jedynym przypadkiem, w którym kiedykolwiek używam reflub outw C #, jest użycie wzorca takiego TryParse(), w którym metoda zwraca wynik logiczny, a jedynym sposobem na uzyskanie z niej przeanalizowanej wartości jest użycie reflub out.
Robert Harvey

3
Zgadnij co, to prawda, czego potrzebuję! ;)
pożarł elysium

Innym sposobem jest zwrócenie obiektu złożonego zawierającego zarówno status, jak i wartość null. Ale przyznaję, że to trochę Rube Goldberg.
Robert Harvey

1
Nie ma nic złego w zwracaniu obiektu złożonego, gdyby tylko istniał predefiniowany obiekt użyteczny (tj. Krotki). Ale czekaj, to wymagałoby nieskasowanych typów generycznych pracujących z typami prymitywnymi, aby były wydajne :)
Pavel Minaev

Odpowiedzi:


102

Nie, Java nie ma czegoś takiego jak C # refi outsłowa kluczowe do przekazywania przez referencje.

W Javie można przekazać tylko wartość. Nawet odwołania są przekazywane według wartości. Więcej informacji można znaleźć na stronie Jona Skeeta o przekazywaniu parametrów w Javie .

Aby zrobić coś podobnego reflub outmusiałbyś zawinąć parametry wewnątrz innego obiektu i przekazać to odwołanie do obiektu jako parametr.


5
Na niektórych należy to rozszerzyć. Jako wartość można przekazywać tylko prymitywy (int, short, char itp.). I nie, nie ma wyjścia.
Corey Sunwold

14
To nie jest w 100% prawdą, jeśli przekażesz tablicę lub klasę, odniesienie do tablicy lub obiektu zostanie przekazane przez wartość, możesz zmienić wewnętrzne na tablicę lub obiekt i zostanie to odzwierciedlone w obiekcie wywołującym.
Romain Hippeau

12
@fearofawhackplanet: Hm, chyba że używasz 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
Z perspektywy CLR przekazujesz referencję zarządzaną ( T&) według wartości, tak. Ale C # ma własną terminologię i konkretnie nie obejmuje takich rzeczy, jak wartości typu ref T- z perspektywy C # refjest ściśle modyfikatorem parametru, a mówienie o „przekazywaniu odwołania przez wartość” w odniesieniu do niego nie ma sensu .
Pavel Minaev

30

Odpowiedź bezpośrednia: nie

Ale możesz symulować odniesienie za pomocą opakowań .

I wykonaj następujące czynności:

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

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

Na zewnątrz

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

I w zasadzie każdy inny typ, taki jak:

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

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

28
Auć. To jest brzydkie.
pożarł elizjum

46
Nigdy nie powiedziałem, że możesz to zrobić w ten elegancki sposób : P
OscarRyz

dlaczego więc nie działa: private static void ParseLine (String nowa linia, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (nowa linia); aWrapper [0] = st.nextToken (); bWrapper [0] = new Integer (st.nextToken ()); } ParseLine (nowa linia, nowy ciąg [] {a}, nowa liczba całkowita [] {b});
Elad Benda

3
@ user311130 Twój kod jest trudny do odczytania, ale możesz utworzyć nowe pytanie o treści w rodzaju: „Znalazłem tę odpowiedź <link do mojej odpowiedzi>, ale poniższy kod nie działa <Twój kod tutaj>
OscarRyz

To paskudne, ale nadal to lubię, ponieważ rozwiązuje problem w "najczystszy" sposób, jaki mogę wymyślić dla Javy w tej chwili ...
Pangamma

8

W rzeczywistości, o ile wiem, nie ma odpowiedników słów kluczowych ref ani out w języku Java . Jednak właśnie przekształciłem kod C # w Javę, który używa parametru out i doradzi, co właśnie zrobiłem. Każdy obiekt należy opakować w klasę opakowania i przekazać wartości opakowane w instancję obiektu opakowania w następujący sposób;

Prosty przykład użycia Wrappera

Oto klasa 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;
    }
}

A oto kod testowy;

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!";
    }

}

Przykład z prawdziwego świata

Metoda AC # .NET przy użyciu parametru out

Tutaj jest metoda C # .NET , która używa słowa kluczowego out ;

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

Odpowiednik Java kodu C #, który używa parametru out

A odpowiednik tej metody w Javie z pomocą klasy opakowania jest następujący;

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

Klasa opakowania

A klasa wrapper stosowane w tym kodzie Java jest jak poniżej;

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

}

zobacz poprzednie odpowiedzi.
pashute

Udzielenie szczegółowej odpowiedzi negatywnej jest zabawne. Sprawdziłem SO i nie mogłem znaleźć dokładnej odpowiedzi z kodem demonstracyjnym, dlatego napisałem tę odpowiedź, o ile pamiętam. Jeśli czekasz na pojedynczą odpowiedź i głosujesz w dół na każdą inną odpowiedź, SO powinien zablokować wszystkie odpowiedzi inne niż te, które otrzymały potwierdzenie od właściciela pytania.
Levent Divilioglu

OK, mogę usunąć głos negatywny tylko wtedy, gdy edytujesz odpowiedź. Przeczytaj więc pozostałe odpowiedzi i wyjaśnij, dlaczego nie chciałeś korzystać z tych rozwiązań. Opakowanie (aw szczególności opakowanie REF i OUT tylko dla zabawy). 5 osób udzieliło krótkich i pełnych odpowiedzi wraz z przykładami , a tylko Eyal zasadniczo napisał: „Nie, nie możesz”.
pashute

1
Ta odpowiedź wygląda dobrze. Jest podobna do pierwszej odpowiedzi, ale zawiera w pełni szczegółowe przykłady użycia klasy opakowania w Javie.
hubatish

8

Java przekazuje parametry według wartości i nie ma żadnego mechanizmu umożliwiającego przekazywanie przez odwołanie. Oznacza to, że za każdym razem, gdy przekazywany jest parametr, jego wartość jest kopiowana do ramki stosu obsługującej wywołanie.

Termin wartość, którego tutaj używam, wymaga niewielkiego wyjaśnienia. W Javie mamy dwa rodzaje zmiennych - prymitywy i obiekty. Wartość prymitywu jest samym prymitywem, a wartość obiektu jest jego odniesieniem (a nie stanem obiektu, do którego się odwołuje). Dlatego każda zmiana wartości wewnątrz metody spowoduje tylko zmianę kopii wartości na stosie i nie będzie widoczna dla wywołującego. Na przykład nie ma sposobu na zaimplementowanie prawdziwej metody swap, która odbiera dwa odwołania i zamienia je (nie ich zawartość!).


6

Podobnie jak wiele innych, musiałem przekonwertować projekt C # na Javę. Nie znalazłem w sieci pełnego rozwiązania dotyczącego modyfikatorów out i ref . Ale udało mi się zebrać informacje, które znalazłem i rozszerzyć je, aby stworzyć własne zajęcia, aby spełnić wymagania. Chciałem dokonać rozróżnienia między parametrami ref i out dla przejrzystości kodu. Z poniższymi klasami jest to możliwe. Niech ta informacja zaoszczędzi innym czasu i wysiłku.

Przykład znajduje się w poniższym kodzie.

//*******************************************************************************************
//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 nie ma na to standardowego sposobu. Większość zamiany zostanie dokonana na liście spakowanej w klasie. ale jest na to nieoficjalny sposób:

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
To nie daje ci semantyki referencyjnej dla parametru, to po prostu użycie odbicia do zmutowania obiektu, który został zaprojektowany jako niezmienny, co jest okropnym pomysłem i nadal nie zapewnia semantyki referencyjnej dla parametru.
Servy
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.