2010-07-25 31 views
40

Tôi hy vọng rằng ai đó có thể làm rõ những gì đang xảy ra ở đây cho tôi. Tôi đào xung quanh trong lớp số nguyên cho một chút nhưng vì số nguyên là ghi đè toán tử + Tôi không thể tìm ra điều gì đang xảy ra. Vấn đề của tôi là với dòng này:Làm thế nào tôi có thể vượt qua một lớp Integer một cách chính xác bằng cách tham khảo?

Integer i = 0; 
i = i + 1; // ← I think that this is somehow creating a new object! 

Đây là lập luận của tôi: Tôi biết rằng java là đi qua giá trị (or pass by value of reference), vì vậy tôi nghĩ rằng trong ví dụ sau đối tượng số nguyên sẽ được tăng lên mỗi lần.

public class PassByReference { 

    public static Integer inc(Integer i) { 
     i = i+1; // I think that this must be **sneakally** creating a new integer... 
     System.out.println("Inc: "+i); 
     return i; 
    } 

    public static void main(String[] args) { 
     Integer integer = new Integer(0); 
     for (int i =0; i<10; i++){ 
      inc(integer); 
      System.out.println("main: "+integer); 
     } 
    } 
} 

Đây là kết quả mong muốn của tôi:

 
Inc: 1 
main: 1 
Inc: 2 
main: 2 
Inc: 3 
main: 3 
Inc: 4 
main: 4 
Inc: 5 
main: 5 
Inc: 6 
main: 6 
... 

Đây là sản phẩm thực tế.

 
Inc: 1 
main: 0 
Inc: 1 
main: 0 
Inc: 1 
main: 0 
... 

Tại sao nó hoạt động như thế này?

+0

câu hỏi liên quan: http://stackoverflow.com/questions/4520137/does-java-have-mutable-types-for-integer-float-double-long – koppor

Trả lời

42

Có hai vấn đề:

  1. Integer là đi qua giá trị, chứ không phải bằng cách tham khảo. Việc thay đổi tham chiếu bên trong một phương thức sẽ không được phản ánh vào tham chiếu được truyền vào trong phương thức gọi.
  2. Số nguyên là không thay đổi. Không có phương pháp như vậy như Integer#set(i). Nếu không, bạn có thể sử dụng nó.

Để làm cho nó hoạt động, bạn cần gán lại giá trị trả về của phương thức inc().

integer = inc(integer); 

Để tìm hiểu thêm một chút về đi ngang qua giá trị, đây là một ví dụ khác:

public static void main(String... args) { 
    String[] strings = new String[] { "foo", "bar" }; 
    changeReference(strings); 
    System.out.println(Arrays.toString(strings)); // still [foo, bar] 
    changeValue(strings); 
    System.out.println(Arrays.toString(strings)); // [foo, foo] 
} 
public static void changeReference(String[] strings) { 
    strings = new String[] { "foo", "foo" }; 
} 
public static void changeValue(String[] strings) { 
    strings[1] = "foo"; 
} 
+11

Nó không thực sự vượt qua bởi giá trị. Nó đi qua giá trị của tham chiếu .... là thứ duy nhất được truyền cho các đối tượng là một địa chỉ bộ nhớ. – bwawok

+22

Nói đúng, đối với tham chiếu, đó là "chuyển tham chiếu theo giá trị" và đối với nguyên thủy, nó chỉ là "truyền theo giá trị". – BalusC

+2

tốt, một bản sao tham chiếu được gửi vào hàm –

2

tôi nghĩ rằng nó là autoboxing được ném bạn xuống.

này một phần của mã của bạn:

public static Integer inc(Integer i) { 
     i = i+1; // I think that this must be **sneakally** creating a new integer... 
     System.out.println("Inc: "+i); 
     return i; 
    } 

Thật nắm để mã mà trông giống như:.

public static Integer inc(Integer i) { 
     i = new Integer(i) + new Integer(1);  
     System.out.println("Inc: "+i); 
     return i; 
    } 

Trong đó tất nhiên .. sẽ không thay đổi tài liệu tham khảo thông qua vào

Bạn có thể sửa nó với một cái gì đó như thế này

public static void main(String[] args) { 
     Integer integer = new Integer(0); 
     for (int i =0; i<10; i++){ 
      integer = inc(integer); 
      System.out.println("main: "+integer); 
     } 
    } 
+1

huh, ok thật thú vị. Cảm ơn cho thuật ngữ tự động đấm bốc. Bất biến cũng có vẻ khá quan trọng. Chúc bạn một ngày cuối tuần vui vẻ! – sixtyfootersdude

+1

Ya tra cứu hộp số tự động. Quay trở lại Java 1.4 bạn chỉ có thể thêm int với ints, hoặc Integers với số nguyên. Thực tế bạn có thể làm theo bất kỳ cách nào bây giờ có thể hơi khó để khái niệm hóa chỉ là nhìn vào mã. – bwawok

9

Những gì bạn nhìn thấy ở đây không phải là một bộ thu phát + bị quá tải, nhưng hành vi tự động.Lớp Integer là không thay đổi và mã của bạn:

Integer i = 0; 
i = i + 1; 

được xem bởi trình biên dịch (sau khi autoboxing) như:

Integer i = Integer.valueOf(0); 
i = Integer.valueOf(i.intValue() + 1); 

vì vậy bạn là chính xác trong kết luận của bạn rằng dụ Integer được thay đổi, nhưng không lén lút - đó là phù hợp với định nghĩa ngôn ngữ Java :-)

2

Nếu bạn thay đổi chức năng inc() của bạn để này

public static Integer inc(Integer i) { 
     Integer iParam = i; 
     i = i+1; // I think that this must be **sneakally** creating a new integer... 
     System.out.println(i == iParam); 
     return i; 
    } 

thì bạn sẽ thấy rằng nó luôn in "sai". Điều đó có nghĩa là phần bổ sung tạo ra một phiên bản Integer mới và lưu trữ nó trong biến số địa phương i ("cục bộ", bởi vì tôi thực sự là một bản sao của tham chiếu đã được chuyển), để lại biến của phương thức gọi không bị ảnh hưởng.

Số nguyên là một lớp không thay đổi, có nghĩa là bạn không thể thay đổi giá trị của nó nhưng phải có một phiên bản mới. Trong trường hợp này, bạn không phải thực hiện theo cách thủ công như sau:

i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1); 

thay vào đó, nó được thực hiện bằng cách tự động.

15

Số nguyên là không thay đổi. Bạn có thể bọc int trong lớp wrapper tùy chỉnh của bạn.

class WrapInt{ 
    int value; 
} 

WrapInt theInt = new WrapInt(); 

inc(theInt); 
System.out.println("main: "+theInt.value); 
+2

Tại sao số nguyên được tạo thành bất biến? –

+1

@RahulJain Không chắc chắn, tôi nghi ngờ nó là phù hợp với loại giá trị thực tế ('int'), vì vậy cả hai đều có cùng ngữ nghĩa. – Markos

+1

Bạn có ý nghĩa gì bằng cách nhất quán. int có thể được thay đổi nhưng không phải là Integer, Đây là nhược điểm của lớp wrapper. –

5

Bạn đang đúng ở đây:

Integer i = 0; 
i = i + 1; // <- I think that this is somehow creating a new object! 

Đầu tiên: Integer là không thay đổi.

Thứ hai: Lớp Số nguyên không ghi đè toán tử +, có tự động phát và tự động liên quan đến dòng đó (Trong các phiên bản Java cũ hơn bạn sẽ gặp lỗi trên dòng trên).
Khi bạn viết i + 1 trình biên dịch đầu tiên chuyển đổi số nguyên thành (nguyên thủy) int để thực hiện bổ sung: tự động hộp thư. Tiếp theo, thực hiện i = <some int> trình biên dịch chuyển đổi từ int thành Số nguyên (mới): hộp số tự động.
Vì vậy, + thực sự đang được áp dụng cho nguyên mẫu int s.

12

Có 2 cách để vượt qua bằng cách tham khảo

  1. Sử dụng org.apache.commons.lang.mutable.MutableInt từ thư viện Apache Commons.
  2. Tạo lớp tùy chỉnh như hình dưới đây

Dưới đây là một mẫu mã để làm điều đó:

public class Test { 
    public static void main(String args[]) { 
     Integer a = new Integer(1); 
     Integer b = a; 
     Test.modify(a); 
     System.out.println(a); 
     System.out.println(b); 

     IntegerObj ao = new IntegerObj(1); 
     IntegerObj bo = ao; 
     Test.modify(ao); 
     System.out.println(ao.value); 
     System.out.println(bo.value); 
    } 


    static void modify(Integer x) { 
     x=7; 
    } 
    static void modify(IntegerObj x) { 
     x.value=7; 
    } 
} 

class IntegerObj { 
    int value; 
    IntegerObj(int val) { 
     this.value = val; 
    } 
} 

Output:

1 
1 
7 
7 
+2

thx rất nhiều tôi đã sử dụng 'MutableInt' –

6

câu trả lời tốt ở trên giải thích các câu hỏi thực tế từ OP.

Nếu bất kỳ ai cần phải vượt qua một số cần được cập nhật toàn cầu, hãy sử dụng AtomicInteger() thay vì tạo các lớp trình bao khác nhau được đề xuất hoặc dựa vào libs của bên thứ ba.

Các AtomicInteger() là khóa học chủ yếu được sử dụng để truy cập an toàn luồng nhưng nếu hiệu suất truy cập không có vấn đề, tại sao không sử dụng lớp tích hợp này. Các tiền thưởng thêm là tất nhiên an toàn chủ đề rõ ràng.

import java.util.concurrent.atomic.AtomicInteger 
Các vấn đề liên quan