2010-02-05 52 views
25
package myintergertest; 

/** 
* 
* @author Engineering 
*/ 
public class Main { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     //this one does not increment 
     Integer n = new Integer(0); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 

     //this one will increment 
     MyIntegerObj myInt = new MyIntegerObj(1); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 

    } 

    public static void Increment(Integer n) { 
     //NO. this doesn't work because a new reference is being assigned 
     //and references are passed by value in java 
     n++; 
    } 

    public static void Increment(MyIntegerObj n) { 
     //this works because we're still operating on the same object 
     //no new reference was assigned to n here. 
     n.plusplus(); //I didn't know how to implement a ++ operator... 
    } 
} 

Kết quả cho tất cả là n = 0. Số nguyên n là một đối tượng và do đó được truyền qua tham chiếu, vậy tại sao số tăng không phản ánh lại trong phương thức người gọi (chính)? Tôi dự kiến ​​đầu ra là n = 0 n = 1 n = 2 v.v ...Cách tăng một lớp Giá trị tham chiếu nguyên trong java từ phương thức khác

CẬP NHẬT: Lưu ý Tôi đã cập nhật ví dụ mã ở trên. Nếu tôi hiểu đúng, Jon Skeet đã trả lời câu hỏi tại sao myInt sẽ tăng lên và tại sao n lại không. Đó là bởi vì n đang nhận được một tham chiếu mới được gán trong phương thức Increment. Nhưng myInt KHÔNG được gán một tham chiếu mới vì nó gọi hàm thành viên.

Điều đó có vẻ như tôi hiểu chính xác lol?

Trả lời

36

Không, đối tượng không được chuyển theo tham chiếu. Các tham chiếu được truyền theo giá trị - có sự khác biệt lớn. Integer là một loại bất biến, do đó bạn không thể thay đổi giá trị trong phương thức.

tuyên bố n++; của bạn là một cách hiệu quả

n = Integer.valueOf(n.intValue() + 1); 

Vì vậy, mà gán một giá trị khác nhau để biến n trong Increment - nhưng như Java chỉ có pass-by-value, điều đó không ảnh hưởng đến giá trị của n trong phương thức gọi điện.

EDIT: Để trả lời cập nhật của bạn: đúng vậy. Có lẽ loại "MyIntegerObj" của bạn có thể thay đổi và thay đổi trạng thái bên trong khi bạn gọi plusplus(). Ồ, và đừng bận tâm tìm kiếm cách thực hiện một toán tử - Java không hỗ trợ các toán tử do người dùng định nghĩa.

+0

Ý của bạn là nói '** Đối tượng ** được chuyển bởi giá trị - ...'? – FrustratedWithFormsDesigner

+8

Không, anh không làm thế.Nguyên thủy và tham chiếu được truyền theo giá trị. Các đối tượng không được truyền; họ sống trên đống. – danben

+0

Tôi nghĩ rằng tôi hiểu, xem các cập nhật của tôi và cho tôi biết. cảm ơn!!! – cchampion

6

Bạn đang tìm kiếm nội dung nào đó tương tự như tham chiếu của C++ hoặc tham số C# 'out. Java (và nhiều ngôn ngữ khác) không hỗ trợ điều này.

loại hành vi này thường đạt được bằng cách thay đổi phương pháp từ khoảng trống để (trong trường hợp này) int: public static int increment(int n) { return ++n; }

Nếu phương pháp này đã được trở về một cái gì đó thì bạn có thể tạo ra một lớp mới có hai lĩnh vực (có thể thậm chí là công khai): giá trị trả lại ban đầu và giá trị mới của n.

Hoặc, bạn cũng có thể bọc số nguyên bên trong một lớp Ô chung chung. Bạn chỉ cần phải viết lớp tế bào này một lần:

public class Cell<T> { 
    public Cell(T t) { value = t; } 
    public void set(T t) { value = t; } 
    public T get() { return value; } 
} 

Sau đó bạn có thể sử dụng nó trong tất cả các tình huống mà bạn muốn một phương pháp để thay đổi một nguyên thủy:

public static void increment(Cell<Integer> n) { 
    n.set(n.get()++); 
} 

Cell<Integer> n = new Cell<Integer>(0); 
increment(n); 
increment(n); 
increment(n); 
System.out.println(n.get()); // Output: 3 
22

Bạn có thể sử dụng một AtomicInteger từ java.util.concurrent.atomic . Nó có phương pháp 'incrementAndGet' phù hợp với giới thiệu của bạn.

+0

Cái gì? Có, tôi có nghĩa là nó hoạt động, nhưng nó không thực sự là một ý tưởng tốt. Nó sẽ làm những việc như đồng bộ hóa bộ đệm của bộ vi xử lý của bạn, vì nó không phải là một hoạt động nguyên tử khác. Thực sự, bạn không nên sử dụng phiên bản nguyên tử của loại dữ liệu trừ khi bạn có nhu cầu về nguyên tử đó ... – Jasper

+0

@ Jasper, tôi không thể tranh luận với điều đó. Nó chỉ là một công cụ khác trong hộp công cụ của tôi. Tôi chọn tùy thuộc vào nhiệm vụ trong tầm tay. Và trừ khi profiler chỉ ra một vấn đề tại điểm cụ thể đó, nó vẫn là một lựa chọn. – DerMike

1

Integervalue object có nghĩa là giá trị không thể thay đổi.

Lưu ý rằng n++ tương đương với n = n + 1. Bạn chỉ đang thay đổi giá trị được đề cập bởi biến cục bộ n, không phải giá trị của chính số n.

2

Bạn không thể. Java là đúng giá trị.

Xem http://javadude.com/articles/passbyvalue.htm

sự lựa chọn của bạn là để quấn số nguyên trong một đối tượng (hoặc mảng), vượt qua rằng trong và cập nhật, trả về một giá trị, hoặc thực hiện các nguyên một biến lớp/trường hợp.

Điều nào là tốt hơn tùy thuộc vào tình huống.

3

Như Jon Skeet đã đề cập, tất cả các phương pháp trong Java đều được truyền theo giá trị, chứ không phải thông qua tham chiếu. Vì các kiểu tham chiếu được truyền theo giá trị, những gì bạn có bên trong thân hàm là bản sao của tham chiếu - bản sao này và tham chiếu gốc cả hai đều trỏ đến cùng một giá trị.

Tuy nhiên, nếu bạn gán lại bản sao bên trong nội dung chức năng, nó sẽ không ảnh hưởng đến phần còn lại của chương trình vì bản sao đó sẽ nằm ngoài phạm vi khi chức năng thoát.

Nhưng hãy xem xét rằng bạn không thực sự cần phải có tham chiếu để thực hiện những gì bạn muốn làm. Ví dụ, bạn không cần một phương thức để tăng một số Integer - bạn chỉ có thể tăng nó ở điểm trong mã của bạn, nơi nó cần được tăng lên.

Đối với các loại phức tạp hơn, như đặt một số thuộc tính trên một đối tượng, đối tượng bạn đang làm việc có khả năng thay đổi; trong trường hợp đó một bản sao của tài liệu tham khảo hoạt động tốt, vì dereference tự động sẽ đưa bạn đến đối tượng ban đầu có setter mà bạn đang gọi.

3

Lưu ý phụ: Theo quan điểm khiêm tốn của tôi, viết n ++ trên đối tượng Integer cực kỳ gây hiểu nhầm. Điều này dựa trên một tính năng của Java được gọi là "autoboxing", nơi nó chuyển đổi nguyên thủy cho các đối tượng boxing tương ứng của chúng - như int thành Integer hoặc boolean thành Boolean - tự động và không có cảnh báo. Thành thật mà nói tôi nghĩ rằng đó là một tính năng xấu. Có, đôi khi nó làm cho cuộc sống của bạn đơn giản hơn bằng cách tự động thực hiện chuyển đổi tiện dụng cho bạn, nhưng nó cũng có thể thực hiện các chuyển đổi mà bạn không có ý định và không nhận ra đang xảy ra, với các tác dụng phụ không mong muốn.

Dù sao, có hai cách để thực hiện những gì bạn đang có vẻ như cố gắng để làm:

Một: Có chức năng tăng trở lại một Integer mới với giá trị được cập nhật. Giống như:

public Integer increment(Integer n) 
    { 
     Integer nPlus=Integer.valueOf(n.intValue()+1); 
    } 

sau đó gọi nó với:

Integer n=Integer.valueOf(0); 
n=increment(n); // now n==1 
n=increment(n); // now n==2 

vv

Hai: Tạo lớp của riêng bạn mà giống Integer nhưng đó là có thể thay đổi. Giống như:

class MutableInteger 
{ 
    int value; 
    public MutableInteger(int n) 
    { 
    value=n; 
    } 
    public void increment() 
    { 
    ++value; 
    } 
    ... whatever other functions you need ... 
} 

Tất nhiên điều bắt mắt là bạn phải tái phát minh ra lớp Integer.

2

Như DerMike nêu bạn có thể đạt được điều này như sau:

public void doStuff() { 
    AtomicInteger i = new AtomicInteger(0); 
    increment(i); 
    System.out.println(i); 
} 

    public void increment(AtomicInteger i) { 
    i.set(i.get() + 1); 
} 
Các vấn đề liên quan