Java'da kabul edilen bir yöntem çağrısı uygulamasında 'bu' iletiliyor


93

Geçerli nesneyi bir yöntem çağrısında geçirmek iyi / kötü / kabul edilebilir bir uygulama mı? De olduğu gibi:

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);
    }
}

Özellikle, hat bar.foo(this)kabul edilebilir mi?


60
Bu neden kabul edilemez? Yaygındır.
Denys Séguret

3
Yani ... bu 8 yes :) (Ve evet, güncel
tutuyorum

2
Statik olmayan anonim sınıf, süper sınıfın bu referansını otomatik olarak iletir, böylece bu kabul edilebilir olacaktır. tek önlem, döngüsel referanslara dikkat etmek olacaktır.
Mehul Rathod

5
Yine de bir uyarı var: Bunu yapıcıda geçmemelisiniz çünkü bu, nesnenizi tutarsız bir durumda ortaya çıkarır. İnsanlar bunu genellikle anonim iç sınıflar olarak geri çağırmalar (örn. ActionListener) oluşturduklarında ve ardından bunu başka bir nesneye ilettiklerinde yaparlar.
Tamas Rev

3
@dystroy kabul edilebilir olduğunu kabul etmeme rağmen , yaygın olduğu için kabul edilebilir olduğunu ima etmek gerçekten kötü bir mantık. Ortak olduğu için bir şeyler yapmak sizi çok fazla belaya sokabilir
Carrie Kendall

Yanıtlar:


155

Kullanmamak için hiçbir neden yok this, mevcut örnek ve kullanımı tamamen meşru. Aslında, genellikle onu atlamanın temiz bir yolu yoktur.

Öyleyse kullan.

Örnek olmadan kabul edilebilir olduğuna ikna etmek zor olduğundan (böyle bir soruya olumsuz bir cevap tartışmak her zaman daha kolaydır), en yaygın java.langsınıflardan birini açtım , Stringbirini ve elbette bu kullanımın örneklerini buldum, örneğin

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

(thisBüyük "kabul edilmiş" projeleri arayın , bulmayı başaramazsınız.


6
Mükemmel cevap.
maxf130

15
-1 çünkü çift yönlü sınıf ilişkilerinin tek yönlü ilişkilerden daha karmaşık olduğu gerçeğinden bahsedilmez. Yazılımın olabildiğince açık olmasını sağlamak çok önemlidir. Yukarıdaki spesifik örnekte , iki sınıf arasında iki yönlü bir referansa sahip olmaktan kaçınmak ve davranışı ve verileri bir araya getirmek için Metodu foo'yu Baz sınıfına Taşı daha mantıklı olacaktır .
JW.

36
Bir yanıta -1, çünkü soruda yorum yapabileceğiniz ek küçük bir ayrıntı buldunuz mu? Gerçekten mi ?
Denys Séguret

13
@dystroy Evet, "Kullanmamak için bir neden yok" ifadenize katılmıyorum.
JW.

4
Pratikte, gerçek kodda (OP'nin basitleştirilmiş örneğinin aksine) geçiş this, örneğin kalıtım ve arayüzler nedeniyle çift yönlü bir bağlantı eklemeniz anlamına gelmez.
Denys Séguret

165

Bunda yanlış bir şey yok. Ne iyi bir uygulama değil , bir-henüz-tamamen-başlatılmış değil nesnesine bir başvuru verecekti çünkü aynı iç kurucular yapmaktır.

Burada benzer bir gönderi var: Java bunu yapıcıya sızdırıyor ve ikincisinin neden kötü bir uygulama olduğunu açıklıyor.


18
+1: inşaatçılarda 'buna' atıfta bulunmanın tehlikesini belirtmek güzel.
Bathsheba

Mutlaka kötü bir uygulama değildir. Örneğin, bir Carkurucu Wheelörnekler oluşturabilir , bir Carolmadan Wheeltam olarak başlatılırken, Wheelkarşılık Cargelmeyen bir de tam olarak başlatılamaz. Bu durumda, Otomobilin yapımcısının Wheel'in yapıcısına geçmesi kabul edilebilir this. Diğer alternatif ise hem Araba hem de Tekerleğin özel yapımcısına sahip olması ve Araba, Tekerleği oluşturan ve Tekerleği Arabaya takan bir fabrika işlevini kullanmaktır; ama bu Car'da statik bir yöntem mi yoksa Wheel'de statik bir yöntem mi olmalı?
Lie Ryan

Açıkçası, sizin CarFactoryWheelInstallerProxyiçin tekerlekleri takan bir yaratmalısınız .
Kevin

6
@LieRyan Wheeltamamen bağımlıdır Carve IMO'nun bundan haberi olmamalıdır Car.
Izkata

3
thisBir kurucu içinden kullanmanın YALNIZCA kötü yanı , thishenüz tam olarak oluşturulmamış nesne referansının güvenilmeyen veya bilinmeyen istemcilere (veya bir görünüme sahip olduğunu varsayan istemci kodunun) yayınlandığı bir yönteme veya bağlama aktarılmasıdır. tamamen inşa edilmiş nesne). Bir kurucudan thisortak bir başlatma gerçekleştiren bir özel paket yöntemine geçiş, bence, sadece kabul edilebilir değil, aynı zamanda arzu edilir.
scottb

42

Evet ama iki şeye dikkat etmelisin

  1. Geçme bu nesne oluşturulur alınmayınca henüz (yani yapıcısında)
  2. Bunu uzun ömürlü bir nesneye aktarmak, referansı canlı tutacak ve bu nesnenin çöp toplanmasını engelleyecektir .

1
Java'daki bir kurucunun gerçekten bir kurucu olmadığına dikkat edin, muhtemelen Java'nın yapıcısına "başlatıcı" adını vermek daha uygundur. Java yapıcısı içinde, nesneye aslında bellek tahsis edilmiştir, bu nesne aslında yapıcı içinde zaten mevcuttur / oluşturulmuştur.
Lie Ryan

1
Gerçekte değil, nesne henüz başlatılmamış bazı durum değişkenlerine sahip olabilir, bu nedenle nesne henüz tam olarak çalışmaz. Dolayısıyla, yapıcıda, bunu henüz tüm örnek değişkenlerini başlatmamış olan nesneye bir yöntemi çağırabilen ikinci bir nesneye geçirebilirsiniz.
Stefanos T.

yine de, ikinci nesne aktarılan nesnenin başlatılmadığının farkında olduğu ve ona opak bir nesne olarak davrandığı veya yalnızca böyle bir durumda güvenli olduğu bildirilen çağrı yöntemlerini kullandığı sürece, ona geçişte herhangi bir sorun yaratmayacaktır this. Nesne tahsis edilmemiş olsaydı bunu yapmak imkansız olurdu.
Lie Ryan


5

bu, mevcut nesneyi ifade eder. Yaptığınız şey, sistematik olarak doğru, ancak yöntemi aynı sınıfta çağırıyorsanız buna bir ihtiyaç görmüyorum.


2
Örnek kodda, Baz.method (), parametre olarak Baz örneğiyle Bar.foo () öğesini çağıran bir örnek yöntemdir. Dolayısıyla, OP aynı sınıftaki bir yöntemi çağırmıyor.
kullanıcı

@ MichaelKjörling Juned'in foo () yöntemini Baz sınıfına thistaşıyarak iki sınıf arasında geçiş yapmaya gerek kalmayacağını söylediğini düşünüyorum . Bu nedenle, fazladan karmaşıklığı eklemeye gerek yoktur.
JW.

4

Aynı davranışı elde etmek için daha az karmaşık alternatifler varsa, geçerli nesneyi bir yöntem çağrısında geçirmek kötü bir uygulamadır .

Tanım olarak this, bir nesneden diğerine geçer geçmez çift ​​yönlü bir ilişki oluşturulur .

Martin Fowler'ın Refactoring'den alıntı yapacak olursak:

Çift Yönlü İlişkilendirmeyi Tek Yönlü (200) Olarak Değiştirin

Çift yönlü ilişkilendirmeler kullanışlıdır, ancak bir bedeli vardır. Fiyat, iki yönlü bağlantıları sürdürmenin ve nesnelerin uygun şekilde oluşturulup kaldırılmasını sağlamanın ek karmaşıklığıdır. Çift yönlü ilişkilendirmeler birçok programcı için doğal değildir, bu nedenle genellikle bir hata kaynağıdırlar

...

İhtiyaç duyduğunuzda çift yönlü ilişkileri kullanmalısınız, ihtiyacınız olmadığında kullanmamalısınız. Çift yönlü bir ilişkinin artık ağırlığını çekmediğini görür görmez, gereksiz ucu bırakın.

Bu yüzden teorik olarak, geçmemiz gerektiğini anladığımızda alarm zilleri duymalıyız ve elimizdeki thissorunu çözmek için başka yollar düşünmek için gerçekten çok uğraşmalıyız. Elbette, sonunda çare olarak bunu yapmanın mantıklı olduğu zamanlar vardır.

Ayrıca, genel bir iyileştirme için kodunuzun daha uzun vadede yeniden düzenlenmesi sırasında tasarımınızı geçici olarak bozmak, 'kötü pratik şeyler' yapmak gerekir. (Bir adım geri, iki adım ileri).

Pratikte veba gibi çift yönlü bağlantılardan kaçınarak kodumun büyük ölçüde geliştiğini gördüm .


Basitleştirilmiş bir örneği çift yönlü bir bağlantı kurma ihtiyacıyla karıştırıyorsunuz. Bunu, java.lang kaynak kodunun birçok örneğinde olduğu gibi (örneğin, cevabımda gördüğünüz) parametre olarak geçirmek, çift yönlü bir bağımlılık eklediğiniz anlamına gelmez. Bence bu cevap bir yorum olmalıydı.
Denys Séguret

@dystroy Neden olumsuz oy verdiğinizi açıklayan bir yorum eklediğiniz için teşekkür ederiz. Bilmek her zaman iyidir. Cevabımı, tanım gereği iki yönlü bir ilişkinin thisgeçer geçmez oluşturulduğunu açıklığa kavuşturmak için değiştireceğim .
JW.

1
"tanım gereği, bu geçilir geçmez çift yönlü bir ilişki oluşturulur" . Bu, nerede anlayamadığınızı netleştirir. Verdiğim örneğe bakın. İki yönlü bağlantı yoktur çünkü argümanın türü equalsObject'tir. Bu çok yaygındır: alıcı yöntem, argümanını daha genel bir sınıf veya bir arayüz olarak tanımlar. Böyle bir model java kullanılan nedenden biri olan istenmeyen bağımlılıkları önlemek için. Daha uzun gitmeden önce , saygın java kütüphanelerinde argüman olarak geçmenin birçok oluşumuna bir göz atmanızı öneririm this.
Denys Séguret

Sadece katılmama konusunda anlaşalım. thisMümkün olan yerlerde kodumu girmekten kaçındığım için hayatım çok daha kolay hale geldi . Başkalarına bunu yapmalarını tavsiye ederim.
JW.

2
@JW: Bu cevapta verilen gerekçe alakasız. X'in herhangi bir değeri için daha basit bir şey varsa, X'i yapmak her zaman kötü bir fikirdir.
Lie Ryan

4

Evet. Programlamada geçmesi çok yaygındır, thisancak bunu kullanmanın artıları ve eksileri vardır, yine de bunu yapmak tehlikeli değildir.


Çok sayıda yan etkisi var. Karmaşıklık katar.
JW.

Bu kadar çok yan etki varsa, java kaynak kodumuzda buna benzer tek bir kanıt bulamadık. Kaynak koddan @destroys örneğine bakın.
Suresh Atta

2

Geçişin thisdoğru olduğu ve iyi tasarımı izlediği bir örnek daha eklemek için : Ziyaretçi düzeni . Ziyaretçi tasarım modelinde, yöntem accept(Visitor v)tipik olarak sadece çağırdığı şekilde uygulanır v.visit(this).


1

Kabul edilebilir

Oracle JAVA belgelerinden pasaj:

Bir örnek yöntem veya yapıcı içinde bu, geçerli nesneye - yöntemi veya yapıcısı çağrılan nesneye bir referanstır. Bunu kullanarak bir örnek yöntemi veya bir yapıcı içinden geçerli nesnenin herhangi bir üyesine başvurabilirsiniz.

Bunu bir Alanla kullanma

This anahtar sözcüğünü kullanmanın en yaygın nedeni, bir alanın bir yöntem veya yapıcı parametresi tarafından gölgelendirilmesidir.


2
" Mevcut nesnenin herhangi bir üyesine başvurabilirsiniz " - bu, " parametre olarak geçmekthis kabul edilebilir mi? " Sorusuna yanıt vermiyor gibi görünüyor .
kullanıcı

2
Bu, this.some_variableyerel bir değişken yerine sınıf değişkenine başvurmanın nasıl yapılacağını söylüyor . thisParametre olarak geçişle ilgisi yoktur .
Jose Salvatierra

0

Java'daki her şey değere göre aktarılır. Ancak nesneler ASLA yönteme aktarılmaz!
Java bir nesneyi bir yönteme ilettiğinde, önce nesnenin kendisinin bir kopyasını değil, nesneye bir referansın bir kopyasını oluşturur. Dolayısıyla bu, javada çok kullanılan bir yöntemdir. Ve en sık takip edilen kullanım.


9
Bu konuya benziyor.
Denys Séguret

4
"Java'daki her şey değere göre aktarılır." - bu çok yanıltıcı bir ilk yorum. Aslında tüm nesneler referans olarak aktarılır ve tüm ilkel türler değere göre aktarılır. thisİlkel bir türe ASLA atıfta bulunmazsınız ve bu nedenle "daha fazla bilginizin" kafa karışıklığı yarattığını düşünüyorum.
Stewart

1
@Stewart: Tüm nesnelerin kopyalandığını kastetmediğini hemen açıkça ortaya koyuyor.
LarsH

10
@Stewart bir nesneleri vardır olup , referans ile geçirilir, daha çok nesne referansları değer geçirilir. Bu önemli bir ayrım - referansla geçiş, bir nesneye atıfta bulunan ve bu değişkeni başka bir yönteme aktaran yerel bir değişkene sahipsem, yöntemin değişkenimin hangi nesneye atıfta bulunduğunu değiştirebileceği anlamına gelir ve bu kesinlikle bir şey değildir Java'da yapabilirsiniz. Yöntem olabilir referans kendi didik nesnenin durumunu mutasyona ama başka bir şey noktasına referans kopyamı değiştiremez.
Ian Roberts

2
@Stewart yoda.arachsys.com/csharp/parameters.html , her ikisini de destekleyen C # bağlamında, referansla geçiş ile referansları değere göre geçirme arasındaki farkı açıklayan iyi bir makaledir.
Ian Roberts
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.