Đang chuyển 'this' trong một thực hành được chấp nhận gọi phương thức trong java


93

Thực hành tốt / xấu / chấp nhận được để truyền đối tượng hiện tại trong một cuộc gọi phương thức. Như trong:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

Cụ thể là dòng bar.foo(this)có chấp nhận được không?


60
Tại sao điều đó không được chấp nhận? Nó phổ biến.
Denys Séguret

3
Vì vậy ... đó là :) (Và có, tôi đang giữ nó cập nhật.) 8 có của
Alex Gittemeier

2
lớp ẩn danh non-static sẽ tự động chuyển tham chiếu này của lớp siêu, vì vậy điều này sẽ được chấp nhận. thận trọng duy nhất sẽ là cẩn thận về các tham chiếu vòng tròn.
Mehul Rathod

5
Tuy nhiên, có một lưu ý: bạn không nên chuyển điều này trong hàm tạo vì điều đó sẽ khiến đối tượng của bạn ở trạng thái không nhất quán. Mọi người thường làm điều đó khi họ tạo lệnh gọi lại (ví dụ: ActionListener) dưới dạng các lớp ẩn danh bên trong và sau đó chuyển nó cho một đối tượng khác.
Tamas Rev

3
@dystroy mặc dù tôi đồng ý nó có thể chấp nhận được, ngụ ý rằng nó có thể chấp nhận được vì nó phổ biến là logic thực sự tồi. Làm điều gì đó vì thông thường của nó có thể làm cho bạn gặp rất nhiều rắc rối
Carrie Kendall

Câu trả lời:


155

Không có lý do gì để không sử dụng nó, đây thislà phiên bản hiện tại và nó hoàn toàn hợp pháp để sử dụng. Trên thực tế, thường không có cách nào rõ ràng để bỏ qua nó.

Vì vậy, hãy sử dụng nó.

Vì thật khó để thuyết phục nó được chấp nhận nếu không có ví dụ (một câu trả lời phủ định cho một câu hỏi như vậy luôn dễ tranh luận hơn), tôi vừa mở một trong những java.langlớp phổ biến nhất , Stringmột lớp , và tất nhiên tôi đã tìm thấy các trường hợp sử dụng này, chẳng hạn

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

Hãy tìm (thisnhững dự án lớn được “chấp nhận”, bạn sẽ không thất bại khi tìm thấy nó.


6
Câu trả lời hoàn hảo.
maxf130

15
-1 vì không có đề cập đến thực tế là các mối quan hệ lớp hai chiều phức tạp hơn mối quan hệ một chiều. Điều quan trọng là đảm bảo rằng phần mềm rõ ràng nhất có thể. Trong ví dụ cụ thể ở trên, sẽ hợp lý hơn nếu Move Method foo đến lớp Baz để tránh có tham chiếu hai chiều giữa hai lớp và mang hành vi và dữ liệu lại với nhau.
JW.

35
-1 cho một câu trả lời bởi vì bạn tìm thấy trong câu hỏi một chi tiết nhỏ bổ sung mà bạn có thể nhận xét? Có thật không ?
Denys Séguret

13
@dystroy Có Tôi không đồng ý với câu mở đầu của bạn: "Không có lý do gì để không sử dụng nó".
JW.

4
Trên thực tế, trong mã thực (trái với ví dụ đơn giản của OP), việc chuyển thiskhông có nghĩa là bạn thêm liên kết hai chiều, vì tính kế thừa và giao diện chẳng hạn.
Denys Séguret

165

Không có gì sai với điều đó. Điều KHÔNG PHẢI là một phương pháp hay là thực hiện cùng một hàm tạo bên trong, bởi vì bạn sẽ cung cấp một tham chiếu đến một đối tượng chưa được khởi tạo hoàn toàn.

Có một loại bài đăng tương tự ở đây: Java làm rò rỉ điều này trong phương thức khởi tạo , nơi họ đưa ra lời giải thích về lý do tại sao phương thức sau là một hoạt động không tốt.


18
+1: tốt để chỉ ra mối nguy hiểm khi đề cập đến 'điều này' trong các trình xây dựng.
Bathsheba

Nó không nhất thiết phải là một thực hành xấu. Ví dụ, một phương thức Carkhởi tạo có thể tạo ra các Wheelthể hiện, a Carkhông có Wheelsẽ được khởi tạo không hoàn toàn, trong khi a Wheelkhông có tương ứng Carcũng sẽ được khởi tạo không hoàn toàn. Trong trường hợp này, nó có thể được chấp nhận trong cấu tạo của Xe để chuyển thischo cấu tạo của Bánh xe. Giải pháp thay thế khác sẽ là làm cho cả Ô tô và Bánh xe đều có nhà xây dựng tư nhân và sử dụng một chức năng của nhà máy để cấu tạo Ô tô, Bánh xe và lắp đặt Bánh xe trên Ô tô; nhưng đó phải là phương thức tĩnh trên Xe hay phương thức tĩnh trên Bánh xe?
Lie Ryan

Rõ ràng, bạn nên tạo một CarFactoryWheelInstallerProxycái lắp bánh xe cho bạn.
Kevin

6
@LieRyan Wheelhoàn toàn phục tùng Carvà IMO hoàn toàn không nên biết về điều đó Car.
Izkata

3
Điều tồi tệ DUY NHẤT khi sử dụng thistừ bên trong một hàm tạo là nếu thisđược truyền vào một phương thức hoặc ngữ cảnh mà từ đó tham chiếu đối tượng chưa được cấu trúc đầy đủ được xuất bản cho các máy khách không đáng tin cậy hoặc không xác định (hoặc mã khách giả định rằng nó có chế độ xem đối tượng được xây dựng hoàn chỉnh). Theo tôi, việc chuyển thistừ một phương thức khởi tạo sang một phương thức gói-riêng thực hiện một khởi tạo chung, theo ý kiến ​​của tôi, không chỉ được chấp nhận mà còn là mong muốn.
scottb

42

, nhưng bạn nên cẩn thận về hai điều

  1. Truyền điều này khi đối tượng chưa được xây dựng (tức là trong hàm tạo của nó)
  2. Chuyển nó đến một đối tượng tồn tại lâu dài, điều đó sẽ giữ cho tham chiếu tồn tại và sẽ ngăn đối tượng này bị thu thập rác.

1
Lưu ý rằng một hàm tạo trong Java không thực sự là một hàm tạo, có lẽ thích hợp hơn để gọi hàm tạo của Java là "bộ khởi tạo". Trong hàm tạo Java, đối tượng thực sự đã được cấp phát bộ nhớ, đối tượng này thực sự đã tồn tại / được xây dựng bên trong hàm tạo.
Lie Ryan

1
Không thực sự, đối tượng có thể có một số biến cá thể chưa được khởi tạo, vì vậy đối tượng chưa hoạt động hoàn toàn. Vì vậy, trong phương thức khởi tạo, bạn có thể chuyển nó cho đối tượng thứ hai, đối tượng này có thể gọi một phương thức tới đối tượng chưa khởi tạo tất cả các biến thể hiện của nó.
Stefanos T.

Tuy nhiên, miễn là đối tượng thứ hai biết rằng đối tượng được truyền vào không được khởi tạo và coi nó như một đối tượng không trong suốt hoặc chỉ gọi các phương thức đã được tuyên bố là an toàn ở trạng thái như vậy, nó sẽ không gây ra vấn đề gì khi truyền thisnó. Làm điều đó sẽ là không thể nếu đối tượng không được cấp phát.
Lie Ryan

13

Đó là điều hoàn toàn bình thường và hoàn toàn có thể chấp nhận được.


5

cái này là viết tắt của đối tượng hiện tại. Những gì bạn đang làm là chính xác về mặt tổng hợp nhưng tôi không thấy cần điều này nếu bạn đang gọi phương thức trong cùng một lớp.


2
Trong đoạn mã ví dụ, Baz.method () là một phương thức thể hiện gọi Bar.foo () với thể hiện của Baz như một tham số. Vì vậy OP không gọi một phương thức trong cùng một lớp.
một CVn

@ MichaelKjörling Tôi nghĩ Juned đang nói rằng, bằng cách chuyển phương thức foo () vào lớp Baz, sẽ không cần phải chuyển thisgiữa hai lớp. Vì vậy, không cần phải thêm sự phức tạp thêm.
JW.

4

Việc chuyển đối tượng hiện tại trong một lệnh gọi phương thức là một thực tế không tốt nếu có các lựa chọn thay thế ít phức tạp hơn để đạt được cùng một hành vi.

Theo định nghĩa, liên kết hai chiều được tạo ngay sau khi thisđược truyền từ đối tượng này sang đối tượng khác.

Để trích dẫn Refactoring, bởi Martin Fowler:

Thay đổi kết hợp hai chiều thành một chiều (200)

Các liên kết hai chiều rất hữu ích, nhưng chúng có giá. Cái giá phải trả là sự phức tạp tăng thêm của việc duy trì các liên kết hai chiều và đảm bảo rằng các đối tượng được tạo và xóa đúng cách. Các liên kết hai chiều không phải tự nhiên đối với nhiều lập trình viên, vì vậy chúng thường là nguồn gây ra lỗi

...

Bạn nên sử dụng liên kết hai chiều khi cần nhưng không phải khi không. Ngay khi bạn thấy liên kết hai chiều không còn kéo trọng lượng của nó nữa, hãy bỏ phần cuối không cần thiết.

Vì vậy, về mặt lý thuyết, chúng ta nên nghe thấy tiếng chuông cảnh báo khi chúng ta thấy mình cần phải vượt qua thisvà cố gắng thực sự để nghĩ ra những cách khác để giải quyết vấn đề trong tầm tay. Tất nhiên, có những lúc, cuối cùng, bạn nên làm điều đó.

Ngoài ra, bạn thường phải tạm thời làm hỏng thiết kế của mình, thực hiện 'những việc làm xấu', trong thời gian dài hạn tái cấu trúc mã của bạn để cải thiện tổng thể. (Lùi một bước, tiến hai bước).

Trong thực tế, tôi nhận thấy mã của mình đã được cải thiện hàng loạt bằng cách tránh các liên kết hai chiều như bệnh dịch.


Bạn nhầm lẫn giữa một ví dụ đơn giản với nhu cầu thiết lập một liên kết hai chiều. Chuyển điều này làm tham số, như cần phải rõ ràng với nhiều ví dụ về mã nguồn java.lang (ví dụ: ví dụ bạn đã thấy trong câu trả lời của tôi) không có nghĩa là bạn thêm phụ thuộc hai chiều. Câu trả lời này lẽ ra phải là một bình luận theo quan điểm của tôi.
Denys Séguret

@dystroy Cảm ơn bạn đã thêm một bình luận để giải thích lý do tại sao bạn phản đối. Nó luôn luôn tốt để biết. Tôi sẽ sửa đổi câu trả lời của mình để làm rõ rằng, theo định nghĩa, liên kết hai chiều được tạo ngay sau khi thisđược thông qua.
JW.

1
"theo định nghĩa, liên kết hai chiều được tạo ngay sau khi điều này được thông qua" . Điều này làm rõ nơi bạn không hiểu. Hãy nhìn vào ví dụ tôi đưa ra. Không có liên kết hai chiều vì loại đối số trong equalslà Đối tượng. Điều này rất phổ biến: phương thức nhận định nghĩa đối số của nó như một lớp tổng quát hơn hoặc như một giao diện. Một trong những lý do mẫu như vậy được sử dụng trong java để tránh các phụ thuộc không mong muốn. Trước khi đi lâu hơn, tôi khuyên bạn nên xem qua nhiều lần xuất hiện của việc chuyển thisdưới dạng đối số trong các thư viện java đáng kính.
Denys Séguret

Hãy chỉ đồng ý không đồng ý. Cuộc sống của tôi đã trở nên dễ dàng hơn rất nhiều kể từ khi tôi tránh sử dụng thismã của mình nếu có thể. Tôi muốn khuyên những người khác làm như vậy.
JW.

2
@JW: lý do được đưa ra trong câu trả lời này là không liên quan. Thực hiện X luôn là một ý kiến ​​tồi nếu có thứ gì khác đơn giản hơn, với bất kỳ giá trị nào của X.
Lie Ryan

4

Đúng. bạn có thể sử dụng nó. Nó chỉ phổ biến trong lập trình để vượt qua this. Nhưng vẫn có những ưu và nhược điểm khi sử dụng nó. Tuy nhiên, làm như vậy không nguy hiểm.


Có rất nhiều tác dụng phụ. Nó làm tăng thêm sự phức tạp.
JW.

Nếu có nhiều tác dụng phụ đó, chúng tôi không thể tìm thấy một biện pháp nào như vậy trong mã nguồn java của chúng tôi .see @destroys ví dụ từ mã nguồn.
Suresh Atta

2

Chỉ cần thêm một ví dụ nữa trong đó việc vượt qua thislà chính xác và tuân theo thiết kế tốt: Mẫu khách truy cập . Trong kiểu thiết kế khách truy cập, phương thức accept(Visitor v)thường được triển khai theo cách mà nó chỉ gọi v.visit(this).


1

Có thể chấp nhận được

Đoạn mã từ tài liệu Oracle JAVA:

Trong một phương thức thể hiện hoặc một phương thức khởi tạo, đây là một tham chiếu đến đối tượng hiện tại - đối tượng có phương thức hoặc hàm tạo đang được gọi. Bạn có thể tham chiếu đến bất kỳ thành viên nào của đối tượng hiện tại từ bên trong một phương thức thể hiện hoặc một phương thức khởi tạo bằng cách sử dụng nó.

Sử dụng cái này với một trường

Lý do phổ biến nhất để sử dụng từ khóa this là vì một trường bị che bởi một phương thức hoặc tham số hàm tạo.


2
"Bạn có thể tham chiếu đến bất kỳ thành viên nào của đối tượng hiện tại " - điều đó dường như không trả lời được câu hỏi "có chấp nhận được truyền thisdưới dạng tham số không? ".
một CVn

2
Điều này nói lên cách bạn có thể làm this.some_variableđể tham chiếu đến biến lớp thay vì đến một biến cục bộ. Nó không liên quan gì đến việc chuyển thisdưới dạng một tham số.
Jose Salvatierra

0

Mọi thứ trong java đều được truyền theo giá trị. Nhưng các đối tượng KHÔNG BAO GIỜ được chuyển cho phương thức!
Khi java chuyển một đối tượng đến một phương thức, trước tiên nó tạo một bản sao của một tham chiếu đến đối tượng, chứ không phải một bản sao của chính đối tượng. Do đó, đây là phương pháp được sử dụng hoàn hảo trong java. Và cách sử dụng phổ biến nhất sau đây.


9
Đây là chủ đề.
Denys Séguret

4
"Mọi thứ trong java đều được truyền theo giá trị." - đó là một nhận xét ban đầu rất sai lầm. Trên thực tế, tất cả các đối tượng được truyền bằng tham chiếu và tất cả các kiểu nguyên thủy được truyền bằng giá trị. Bạn KHÔNG BAO GIỜ có thistham chiếu đến kiểu nguyên thủy, và do đó tôi nghĩ rằng "thông tin thêm" của bạn đang gây nhầm lẫn.
Stewart

1
@Stewart: Anh ấy ngay lập tức nói rõ rằng anh ấy không có nghĩa là toàn bộ đối tượng được sao chép.
LarsH

10
@Stewart không, các đối tượng không được truyền bằng tham chiếu, thay vào đó, các tham chiếu đối tượng được chuyển bằng giá trị. Đó là một điểm khác biệt quan trọng - chuyển bằng tham chiếu có nghĩa là nếu tôi có một biến cục bộ tham chiếu đến một đối tượng và chuyển biến đó sang một phương thức khác, phương thức sẽ có thể thay đổi đối tượng mà biến của tôi tham chiếu đến và đây chắc chắn không phải là một cái gì đó bạn có thể làm trong Java. Phương thức có thể thay đổi trạng thái của đối tượng thông qua bản sao tham chiếu của chính nó nhưng nó không thể thay đổi bản sao tham chiếu của tôi để trỏ đến một thứ khác.
Ian Roberts

2
@Stewart yoda.arachsys.com/csharp/parameters.html là một bài viết hay giải thích sự khác biệt giữa chuyển theo tham chiếu và chuyển tham chiếu theo giá trị, trong ngữ cảnh C # hỗ trợ cả hai.
Ian Roberts
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.