Mục lục bài họcĐang ở d01-b2
← AP Computer Science A
0/16 bài đã học xong
Chương 1 · Unit 1 — Using Objects and Methods · Bài 2/4 của chương · bài 2/16 của AP Computer Science A

Calling Methods: Parameters and Return Values

Gọi phương thức: tham số và giá trị trả về
← Mục lục bài học
Lý thuyết · English

Arguments are copied in; results come back only through the return value

A method call has three moving parts: the object (or class) you call it on, the arguments you pass, and the value it returns. A method whose header says void returns nothing, so writing double r = box.print(); is a compile-time error, not a run-time surprise.

public static int doubled(int n) {
    n = n * 2;
    return n;
}

Java passes every argument by value. For a primitive that means the method receives a copy of the number, so changing n inside doubled leaves the caller's variable untouched. For an object the copied thing is the reference: the method cannot make your variable point somewhere else, but it can call mutator methods on the object you both share, and those changes are visible to the caller.

The other half of the rule is that a returned value is thrown away unless you store it or use it. doubled(score); on a line by itself compiles and runs, computes the answer, and discards it. You must write score = doubled(score);. Static methods are called on the class (Math.abs(-4)), instance methods on an object (word.length()); mixing the two up is a compile-time error.

Giải thích tiếng Việt

Một lời gọi phương thức có ba phần: gọi trên cái gì (đối tượng hay tên lớp), truyền vào đối số nào, và nhận về giá trị trả về gì. Phương thức khai báo void không trả về gì cả, nên viết double r = box.print(); là lỗi biên dịch, không phải lỗi lúc chạy.

public static int doubled(int n) {
    n = n * 2;
    return n;
}

Java truyền mọi đối số theo giá trị. Với kiểu nguyên thuỷ, phương thức nhận một bản sao của con số, nên đổi n bên trong không đụng tới biến của người gọi. Với đối tượng, cái được sao chép là tham chiếu: phương thức không thể bắt biến của bạn trỏ sang chỗ khác, nhưng nó có thể gọi phương thức làm thay đổi chính đối tượng hai bên cùng dùng, và thay đổi đó người gọi nhìn thấy.

Nửa còn lại của luật: giá trị trả về bị vứt đi nếu bạn không cất hoặc không dùng. Dòng doubled(score); đứng một mình vẫn dịch được, vẫn chạy, vẫn tính ra kết quả — rồi bỏ kết quả đi. Phải viết score = doubled(score);. Phương thức static gọi trên tên lớp (Math.abs(-4)), phương thức thực thể gọi trên đối tượng (word.length()); lẫn hai loại là lỗi biên dịch.

Người gọi: int score = 5; 5 chép doubled: n = n * 2; 10 score vẫn là 5 — hai ô số là hai ô khác nhau Counter c → đối tượng bump(c) chép MŨI TÊN c.increment() ĐỔI được
Đọc hình theo hai tầng. Tầng trên là kiểu nguyên thuỷ: hai ô số tách rời, nên phương thức muốn đổi kiểu gì cũng không với tới biến gốc — muốn có kết quả thì phải return rồi gán lại. Tầng dưới là kiểu đối tượng: mũi tên được chép nhưng ô chữ nhật ở cuối mũi tên chỉ có một, nên phương thức gọi mutator vào đó là người gọi thấy ngay. Cùng một luật “truyền theo giá trị”, chỉ khác nhau ở chỗ cái được chép là con số hay là mũi tên.
Ví dụ — cùng một luật, hai kết quả khác nhau

Đoạn mã sau in ra gì? Giả sử Counter là lớp ở Bài 1.

public static int grow(int n) { n = n + 10; return n; }
public static void tick(Counter c) { c.increment(); }

int score = 5;
grow(score);
score = grow(score);
Counter k = new Counter();
tick(k);
tick(k);
System.out.println(score + " " + k.getCount());
Giải.

Dòng grow(score); — phương thức chạy, tính ra 15, rồi giá trị trả về không được cất vào đâu nên bị vứt. score vẫn là 5. Đây là dòng mà đề cố tình đặt vào để tách người đọc kỹ với người đọc lướt.

Dòng score = grow(score); — lần này kết quả 15 được gán lại, nên score thành 15.

Hai dòng tick(k);k là kiểu lớp nên phương thức nhận một bản sao của mũi tên, và mũi tên đó vẫn chỉ vào đúng đối tượng của người gọi. Mỗi lần gọi tăng count thêm 1, hai lần thành 2.

Kết quả in ra: 15 2.

Chỗ dễ trượt là tưởng cả hai trường hợp phải giống nhau. Chúng khác nhau vì grow gán lại chính tham số (không với ra ngoài được), còn tick gọi mutator lên đối tượng dùng chung (với ra ngoài được).

Bẫy hay mất điểm — Gọi một phương thức trả giá trị mà quên hứng kết quả — word.toUpperCase();, grow(score);, Math.abs(x); đứng một mình. Trình biên dịch không kêu một tiếng nào, chương trình chạy bình thường, chỉ là không có gì thay đổi. Cách chặn: hễ tên phương thức không phải void thì dòng gọi nó phải có dấu = ở đầu, hoặc phải nằm bên trong một biểu thức khác (in ra, cộng vào, làm đối số).
Phải nhớ — Kiểu nguyên thuỷ: chép số, phương thức không đổi được biến gốc. Kiểu đối tượng: chép mũi tên, phương thức gọi mutator thì đổi được đối tượng. Không phải void thì phải hứng giá trị trả về, nếu không kết quả bị vứt.

Đọc xong rồi — làm thử ngay

Bài tập của chương Unit 1 — Using Objects and Methods gồm 14 câu trắc nghiệm và 8 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.

Làm bài tập chương →