Java'da ilkeller için başvuruya göre geçiş eşdeğeri nasıl yapılır


116

Bu Java kodu:

public class XYZ {   
    public static void main(){  
        int toyNumber = 5;   
        XYZ temp = new XYZ();  
        temp.play(toyNumber);  
        System.out.println("Toy number in main " + toyNumber);  
    }

    void play(int toyNumber){  
        System.out.println("Toy number in play " + toyNumber);   
        toyNumber++;  
        System.out.println("Toy number in play after increement " + toyNumber);   
    }   
}  

bu çıktı:

 
Oyundaki oyuncak numarası 5  
Artım 6'dan sonra oyundaki oyuncak numarası  
Ana 5'teki oyuncak numarası  

C ++ 'da toyNumbergölgelenmeyi önlemek için değişkeni referans olarak geçirebilirim, yani aşağıdaki gibi aynı değişkenin bir kopyasını oluşturabilirim:

void main(){  
    int toyNumber = 5;  
    play(toyNumber);  
    cout << "Toy number in main " << toyNumber << endl;  
}

void play(int &toyNumber){  
    cout << "Toy number in play " << toyNumber << endl;   
    toyNumber++;  
    cout << "Toy number in play after increement " << toyNumber << endl;   
} 

ve C ++ çıktısı şu olacaktır:

Oyundaki oyuncak numarası 5  
Artım 6'dan sonra oyundaki oyuncak numarası  
Ana 6'daki oyuncak numarası  

Sorum şu - Java'nın referans olarak geçmek yerine değere göre geçirildiği düşünüldüğünde, C ++ koduyla aynı çıktıyı elde etmek için Java'daki eşdeğer kod nedir ?


22
Bu bana bir kopya gibi görünmüyor - sözde yinelenen soru java'nın nasıl çalıştığını ve terimlerin ne anlama geldiğini, yani eğitimle ilgilenirken, bu soru özellikle referansla geçiş davranışına benzeyen bir şeyin nasıl elde edileceğini soruyor, birçok C / C ++ / D / Ada programcıları, java'nın neden tamamen değerinin geçtiğini umursamadan, pratik bir iş yapmak için merak ediyor olabilirler.
DarenW

1
@DarenW Tamamen katılıyorum - yeniden açılması için oy verdim. Oh, siz de aynısını yapmak için yeterince itibarınız var :-)
Duncan Jones

İlkellerle ilgili tartışma oldukça yanıltıcıdır, çünkü soru aynı şekilde referans değerleri için de geçerlidir.
shmosel

"C ++ 'da, gölgelendirmeyi önlemek için toyNumber değişkenini referans olarak geçirebilirim" - bu, yöntemde belirtilen toyNumberdeğişken mainyöntemde kapsam dahilinde olmadığı için gölgeleme değildir play. C ++ ve Java'da gölgeleme, yalnızca kapsamların iç içe geçtiği durumlarda gerçekleşir. Bkz. En.wikipedia.org/wiki/Variable_shadowing .
Stephen C

Yanıtlar:


172

Birkaç seçeneğiniz var. En mantıklı olanı gerçekten ne yapmaya çalıştığınıza bağlıdır.

Seçenek 1: toyNumber'ı bir sınıftaki genel üye değişkeni yapın

class MyToy {
  public int toyNumber;
}

daha sonra yönteminize bir MyToy referansını iletin.

void play(MyToy toy){  
    System.out.println("Toy number in play " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Toy number in play after increement " + toy.toyNumber);   
}

Seçenek 2: Referansla geçmek yerine değeri döndür

int play(int toyNumber){  
    System.out.println("Toy number in play " + toyNumber);   
    toyNumber++;  
    System.out.println("Toy number in play after increement " + toyNumber);   
    return toyNumber
}

Bu seçim, ana olarak çağrı sitesinde küçük bir değişiklik gerektirecektir, böylece toyNumber = temp.play(toyNumber);,.

3. Seçenek: Onu bir sınıf veya statik değişken yapın

İki işlev aynı sınıf veya sınıf örneğindeki yöntemler ise, toyNumber'ı bir sınıf üyesi değişkenine dönüştürebilirsiniz.

Seçenek 4: int türünde tek bir öğe dizisi oluşturun ve bunu iletin

Bu bir hack olarak kabul edilir, ancak bazen satır içi sınıf çağrılarından değer döndürmek için kullanılır.

void play(int [] toyNumber){  
    System.out.println("Toy number in play " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Toy number in play after increement " + toyNumber[0]);   
}

2
Açık bir açıklama ve özellikle istenen etkinin nasıl kodlanacağına dair çok sayıda seçenek sunmak için iyi. Şu anda üzerinde çalıştığım bir şey için doğrudan yararlı! Bu sorunun kapatılmış olması delilik.
DarenW

1
1. seçiminiz iletmeye çalıştığınız şeyi yapsa da, aslında geri dönüp iki kez kontrol etmem gerekti. Soru, referansla geçip geçemeyeceğinizi sorar; yani gerçekten, işleve geçirilen oyuncağın içinde bulunduğu aynı kapsamda baskıları yapmış olmalısınız. Sahip olduğunuz şekilde, baskılar, COURSE'un amacını gerçekten kanıtlamayan artışla aynı kapsamda çalıştırılır. artımdan sonra yazdırılan yerel bir kopya farklı bir değere sahip olacaktır, ancak bu, aktarılan referansın olacağı anlamına gelmez. Yine de, örneğiniz işe yarıyor, ancak asıl noktayı kanıtlamıyor.
zero298

Hayır, bence olduğu gibi doğru. Yukarıdaki cevabımdaki her "play ()" işlevinin, değeri artırmadan önce ve sonra da yazdıran orijinal "play ()" işlevinin yerine geçmesi amaçlanmıştır. Orijinal soru, referans olarak geçtiğini kanıtlayan temelde println () 'ye sahiptir.
laslowh

Seçenek 2 daha fazla açıklama gerektirir: toyNumber = temp.play(toyNumber);Arzu edildiği gibi çalışması için ana arama sitesi olarak değiştirilmelidir .
ToolmakerSteve

1
Bu son derece çirkin ve hilekâr bir his var ... neden bu kadar basit bir şey dilin bir parçası değil?
shinzou

30

Java referansla çağırılmaz, yalnızca değere göre çağrılır

Ancak nesne türünün tüm değişkenleri aslında işaretçilerdir.

Yani bir Değişken Nesne kullanırsanız, istediğiniz davranışı göreceksiniz

public class XYZ {

    public static void main(String[] arg) {
        StringBuilder toyNumber = new StringBuilder("5");
        play(toyNumber);
        System.out.println("Toy number in main " + toyNumber);
    }

    private static void play(StringBuilder toyNumber) {
        System.out.println("Toy number in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Toy number in play after increement " + toyNumber);
    }
}

Bu kodun çıktısı:

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

Bu davranışı Standart kitaplıklarda da görebilirsiniz. Örneğin Collections.sort (); Collections.shuffle (); Bu yöntemler yeni bir liste döndürmez, ancak argüman nesnesini değiştirir.

    List<Integer> mutableList = new ArrayList<Integer>();

    mutableList.add(1);
    mutableList.add(2);
    mutableList.add(3);
    mutableList.add(4);
    mutableList.add(5);

    System.out.println(mutableList);

    Collections.shuffle(mutableList);

    System.out.println(mutableList);

    Collections.sort(mutableList);

    System.out.println(mutableList);

Bu kodun çıktısı:

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)

2
Bu sorunun cevabı değil. Tek bir öğe içeren bir tamsayı dizisi oluşturmayı ve ardından bu öğeyi yöntem içinde değiştirmeyi önerseydi, soruyu yanıtlardı play; ör mutableList[0] = mutableList[0] + 1;. Ernest Friedman-Hill'in önerdiği gibi.
ToolmakerSteve

İlkel olmayanlarla. Nesnenin değeri bir referans değildir. Belki şunu söylemek istediniz: "parametre değeri" "referans" dır. Referanslar değere göre aktarılır.
vlakov

Benzer bir şey yapmaya çalışıyorum ama kodunuzda, yöntem private static void play(StringBuilder toyNumber)- örneğin bir tamsayı döndüren public static int ise nasıl çağırırsınız? Çünkü bir numara döndüren bir yöntemim var ama onu bir yerde çağırmazsam kullanımda değil.
frank17

18

Yapmak

class PassMeByRef { public int theValue; }

daha sonra bunun bir örneğine bir referans iletin. Durumu bağımsız değişkenleri aracılığıyla değiştiren bir yöntemin, özellikle paralel kodda en iyi şekilde kaçınılacağına dikkat edin.


3
Benim tarafımdan olumsuz oy verildi. Bu pek çok düzeyde yanlıştır. Java her zaman değerine göre aktarılır. İstisna yok.
duffymo

14
@duffymo - Elbette istediğiniz gibi olumsuz oy verebilirsiniz - ama OP'nin ne istediğini düşündünüz mü? Referans olarak geçmek ve int yapmak istiyor - ve sadece bu, yukarıdaki örneğe değer olarak bir referansı iletirsem başarılır.
Ingo

7
@duffymo: Ha? Yanılıyorsunuz veya soruyu yanlış anlıyorsunuz. Bu IS Ne OP isteklerini yapmanın Java geleneksel yollardan biri.
ToolmakerSteve

5
@duffymo: Yorumunuz pek çok düzeyde yanlış. SO, faydalı cevaplar ve yorumlar sağlamakla ilgilidir ve sadece (sanırım) programlama tarzınıza ve felsefenize uymadığı için doğru bir cevabı olumsuz olarak değerlendiriyorsunuz. En azından orijinal soruya daha iyi bir açıklama veya hatta daha iyi bir cevap verebilir misiniz?
paercebal

2
@duffymo aynen öyle dedim: değere göre bir ref geçir. Sizce ref by ref, onu yavaşlatan dillerde nasıl başarılır?
Ingo

11

Java'da ilkelleri referans olarak geçiremezsiniz. Nesne türünün tüm değişkenleri elbette işaretçilerdir, ancak biz onlara "referanslar" diyoruz ve ayrıca her zaman değerle aktarılırlar.

Bir ilkeli referans olarak geçirmeniz gereken bir durumda, insanların bazen yaptığı şey, parametreyi bir ilkel tür dizisi olarak bildirmek ve ardından bağımsız değişken olarak tek öğeli bir dizi iletmektir. Böylece bir int [1] başvurusunu iletirsiniz ve yöntemde dizinin içeriğini değiştirebilirsiniz.


1
Hayır - tüm sarmalayıcı sınıfları değişmezdir - nesne oluşturulduktan sonra değiştirilemeyen sabit bir değeri temsil ederler.
Ernest Friedman-Hill

1
"İlkelleri Java'da referans olarak geçemezsiniz": OP, Java ile C ++ (yapabildiğiniz yerde) karşıtlığı oluşturdukları için bunu anlıyor gibi görünüyor.
Raedwald

Ernest'in söylediği "tüm sarmalayıcı sınıfları değişmezdir" ifadesinin JDK'nın yerleşik Tamsayı, Çift, Uzun, vb. İçin geçerli olduğunu belirtmek gerekir. Bu kısıtlamayı, Ingo'nun Cevabının yaptığı gibi, özel sarmalayıcı sınıfınızla atlayabilirsiniz.
user3207158

10

Hızlı bir çözüm için, AtomicInteger veya herhangi bir atomik değişken kullanabilirsiniz; bu, dahili yöntemleri kullanarak yöntem içindeki değeri değiştirmenize izin verir. İşte örnek kod:

import java.util.concurrent.atomic.AtomicInteger;


public class PrimitivePassByReferenceSample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        AtomicInteger myNumber = new AtomicInteger(0);
        System.out.println("MyNumber before method Call:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber After method Call:" + myNumber.get());


    }

     void changeMyNumber(AtomicInteger myNumber) {
        myNumber.getAndSet(100);

    }

}

Çıktı:

MyNumber before method Call:0

MyNumber After method Call:100

Bu çözümü kullanıyorum ve seviyorum çünkü güzel bileşik eylemler sunuyor ve zaten bu atomik sınıfları gelecekte kullanmak isteyebilecek eşzamanlı programlara entegre etmek daha kolay.
RAnders00

2
public static void main(String[] args) {
    int[] toyNumber = new int[] {5};
    NewClass temp = new NewClass();
    temp.play(toyNumber);
    System.out.println("Toy number in main " + toyNumber[0]);
}

void play(int[] toyNumber){
    System.out.println("Toy number in play " + toyNumber[0]);
    toyNumber[0]++;
    System.out.println("Toy number in play after increement " + toyNumber[0]);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.