Bir Arayüzde Oluşturucu?


148

Arayüzde bir kurucu tanımlamanın mümkün olmadığını biliyorum. Ama nedenini merak ediyorum, çünkü bence çok faydalı olabilir.

Dolayısıyla, bu arabirimin her uygulaması için bir sınıftaki bazı alanların tanımlandığından emin olabilirsiniz.

Örneğin, aşağıdaki ileti sınıfını düşünün:

public class MyMessage {

   public MyMessage(String receiver) {
      this.receiver = receiver;
   }

   private String receiver;

   public void send() {
      //some implementation for sending the mssage to the receiver
   }
}

İleti arabirimini uygulayan daha fazla sınıfa sahip olabilmem için bu sınıf için bir arabirim tanımlarsanız, yapıcıyı değil, yalnızca gönderme yöntemini tanımlayabilirim. Peki, bu sınıfın her uygulamasının gerçekten bir alıcı setine sahip olmasını nasıl sağlayabilirim? Gibi bir yöntem kullanırsanız setReceiver(String receiver)bu yöntemin gerçekten çağrıldığından emin olamıyorum. Yapıcıda bunu sağlayabilirim.



2
"Yapıcıda [bu sınıfın her uygulamasının gerçekten bir alıcı seti olduğundan] emin olabilirsiniz." Ama hayır, bunu yapamazsın. Böyle bir kurucu tanımlamak mümkün olsaydı, parametre sadece uygulayıcılarınız için güçlü bir ipucu olurdu - ancak istedikleri takdirde onu görmezden gelmeyi seçebilirlerdi.
Julien Silland

3
@mattb Umm, bu farklı bir dil.
yesennes

Yanıtlar:


129

Açıkladığınız şeylerden bazılarını alarak:

"Bu nedenle, bir sınıftaki bazı alanların bu arabirimin her uygulaması için tanımlandığından emin olabilirsiniz."

"İleti arabirimini uygulayan daha fazla sınıfa sahip olabilmem için bu sınıf için bir arabirim tanımlarsanız, yapıcıyı değil yalnızca gönderme yöntemini tanımlayabilirim"

... bu gereksinimler tam olarak soyut sınıflar içindir.


ancak @Sebi'nin tanımladığı kullanım örneğinin (üst yapıcılardan aşırı yüklenmiş yöntemleri çağırmak) cevabımda açıklandığı gibi kötü bir fikir olduğunu unutmayın.
rsp

44
Matt, bu açıkça doğru, ancak soyut sınıflar insanların miras hiyerarşilerini belirtmenin diğer yollarına bakmasına neden olan tek miras sınırlamasından muzdarip.
CPerkins

6
Bu doğrudur ve Sebi'nin acil sorununu çözebilir. Ancak Java'da arabirimlerin kullanılmasının bir nedeni, birden fazla mirasa sahip olamamanızdır. "Şeyimi" soyut bir sınıf haline getiremediğim bir durumda, başka bir şeyden miras almam gerekiyorsa, sorun devam ediyor. Bir çözüm bulduğumu iddia etmiyorum.
Jay

7
@CPerkins bu doğru olsa da, sadece soyut bir sınıf kullanmanın Sebi'nin kullanım durumunu çözeceğini önermiyorum. Herhangi bir şey varsa Message, send()yöntemi tanımlayan bir arabirim bildirmek en iyisidir ve Sebi Messagearabirimin uygulamaları için bir "temel" sınıf sağlamak istiyorsa , bir AbstractMessagede sağlayın . Soyut sınıflar arayüzlerin yerini almamalı, asla önermeye çalışmıyordu.
matt b

2
Anlaşıldı, Matt. Daha o olmadığını işaret ederek, sizinle savunarak değildi komple op istediği için yedek.
CPerkins

76

Arabirimlerdeki kuruculara izin verdiğinizde karşılaştığınız bir sorun, aynı anda birden fazla arabirim uygulama olasılığından kaynaklanır. Bir sınıf, farklı kurucuları tanımlayan birkaç arabirim uyguladığında, sınıfın her biri yalnızca bir arabirimi karşılayan, ancak diğerlerini değil, birden çok kurucu uygulamak zorunda kalacaktır. Bu kurucuların her birini çağıran bir nesne inşa etmek imkansız olacaktır.

Veya kodda:

interface Named { Named(String name); }
interface HasList { HasList(List list); }

class A implements Named, HasList {

  /** implements Named constructor.
   * This constructor should not be used from outside, 
   * because List parameter is missing
   */
  public A(String name)  { 
    ...
  }

  /** implements HasList constructor.
   * This constructor should not be used from outside, 
   * because String parameter is missing
   */
  public A(List list) {
    ...
  }

  /** This is the constructor that we would actually 
   * need to satisfy both interfaces at the same time
   */ 
  public A(String name, List list) {
    this(name);
    // the next line is illegal; you can only call one other super constructor
    this(list); 
  }
}

1
Dil böyle şeylere izin vererek yapamaz mıclass A implements Named, HashList { A(){HashList(new list()); Named("name");} }
mako

1
"Bir arabirimdeki kurucu" için en yararlı anlam, izin verildiği takdirde new Set<Fnord>(), "Bana" olarak kullanabileceğim bir şey ver "anlamına geldiği takdirde yorumlanabilir Set<Fnord>; yazarı ise Set<T>amaçlanan HashSet<T>go-başka bir şey için özel bir ihtiyaç yoktu şeyler için uygulanması olmak, sonra da tanımlayabiliriz arayüzü new Set<Fnord>()ile eş anlamlı olarak kabul edilebilir new HashSet<Fnord>(). Bir sınıfın birden fazla arabirim uygulaması için herhangi bir sorun ortaya çıkmaz, çünkü new InterfaceName()sadece arabirim tarafından belirlenmiş bir sınıf oluşturur .
supercat

Karşı argüman: A(String,List)kurucunuz atanmış kurucu olabilir A(String)ve A(List)buna ikincil olanlar da olabilir. Kodunuz karşı bir örnek değil, sadece kötü bir kod.
Ben Leggiero

Neden bir uygulamadaki tüm kurucuları çağırıyorsunuz ?! Evet, eğer biri String ve diğeri int ile ctor ile daha fazla Arayüz uygularsa, iki ctor'a ihtiyaç duyar - ama ya da kullanılabilir. Bu geçerli değilse, sınıf her iki arabirimi de uygulamaz. Ne olmuş yani!? (yine de Arayüzlerde ctor olmamasının başka nedenleri vardır).
kai

@kai Hayır, bir örnek oluştururken her iki arabirim yapıcısını çağırması gerekir. Başka bir deyişle: Örneğimde, örneğin hem adı hem de listesi vardır, bu nedenle her örneğin hem adı hem de listeyi başlatması gerekir.
daniel kullmann

13

Bir arayüz, bir API için bir sözleşme tanımlar. Bu, API'nin hem uygulayıcısı hem de kullanıcısının üzerinde anlaştığı bir dizi yöntemdir. Bir arabirimin somutlaştırılmış bir uygulaması yoktur, bu nedenle kurucu yoktur.

Açıkladığınız kullanım örneği, kurucunun bir alt sınıfta uygulanan soyut bir yöntemin bir yöntemini çağırdığı soyut bir sınıfa benzer.

Buradaki asıl sorun, temel kurucu yürütülürken, alt nesnenin henüz inşa edilmemiş olması ve dolayısıyla öngörülemeyen bir durumda olmasıdır.

Özetlemek gerekirse: Eğer alıntı için ebeveyn işlerinde aşırı yöntemleri, çağrı sorun istiyor mindprod :

Genel olarak bir kurucuda nihai olmayan yöntemleri çağırmaktan kaçınmalısınız. Sorun, türetilmiş sınıftaki örnek başlatıcıları / değişken başlangıcı , temel sınıfın yapıcısından sonra gerçekleştirilmesidir .


6

Deneyebileceğiniz bir çalışma getInstance(), arayüzünüzde bir yöntem tanımlamaktır, böylece uygulayıcı hangi parametrelerin ele alınması gerektiğini bilir. Soyut bir sınıf kadar sağlam değildir, ancak bir arayüz olarak daha fazla esnekliğe izin verir.

Ancak bu geçici çözüm, getInstance()bu arabirimin tüm nesnelerini örneklemek için kullanmanızı gerektirir .

Örneğin

public interface Module {
    Module getInstance(Receiver receiver);
}

5

Arabirimde yalnızca alt sınıfta nesne oluşturma sırasında başlatılması gerekmeyen statik alanlar vardır ve arabirim yönteminin alt sınıfta gerçek uygulama sağlaması gerekir.Bu nedenle arabirimde yapıcıya gerek yoktur.

İkinci neden - alt sınıfın nesne oluşturulması sırasında, üst yapıcı çağrılır.Ancak birden fazla arabirim uygulanacaksa, arabirim yapıcısının çağrılması sırasında arabirimin kurucusunun ilk olarak çağıracağı bir çakışma oluşur.


3

Arayüzün her uygulamasının belirli bir alan içerdiğinden emin olmak istiyorsanız , arayüzünüze o alanın alıcısını eklemeniz yeterlidir :

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • kapsüllemeyi kırmayacak
  • arayüzünüzü kullanan herkese Receiver nesnenin sınıfa bir şekilde aktarılması (yapıcı veya ayarlayıcı tarafından)

2

Bir arabirim yöntemlerinde başvurulan olmayan bağımlılıklar, arabirimin uyguladığı bir şey değil, uygulama ayrıntıları olarak kabul edilmelidir. Elbette istisnalar olabilir, ancak kural olarak, arayüzünüzü davranışın beklendiği gibi tanımlamanız gerekir. Belirli bir uygulamanın iç durumu, arayüzün tasarım kaygısı olmamalıdır.


1

Bu soruya bakın için neden (yorumlardan alınmıştır).

Gerçekten böyle bir şey yapmanız gerekiyorsa, bir arabirim yerine soyut bir temel sınıf isteyebilirsiniz.


1

Bunun nedeni, arabirimlerin içindeki yöntem gövdesini tanımlamasına izin vermemesidir.Ancak yapıcıyı tanımlamak için tüm yöntemler için varsayılan olarak soyut değiştiriciyle aynı sınıftaki yapıcıyı tanımlamamız gerekir. Bu yüzden arayüzlerde yapıcı tanımlayamayız.


0

İşte bu Tekniği kullanan bir örnek. Bu spesifik örnekte kod, MyCompletionListenersoyut bir sınıf olarak maskelenen bir arayüz, bir kurucu ile bir arayüz olan bir sahte kullanarak Firebase'e çağrı yapıyor

private interface Listener {
    void onComplete(databaseError, databaseReference);
}

public abstract class MyCompletionListener implements Listener{
    String id;
    String name;
    public MyCompletionListener(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

private void removeUserPresenceOnCurrentItem() {
    mFirebase.removeValue(child("some_key"), new MyCompletionListener(UUID.randomUUID().toString(), "removeUserPresenceOnCurrentItem") {
        @Override
        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {

        }
    });
    }
}

@Override
public void removeValue(DatabaseReference ref, final MyCompletionListener var1) {
    CompletionListener cListener = new CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (var1 != null){
                        System.out.println("Im back and my id is: " var1.is + " and my name is: " var1.name);
                        var1.onComplete(databaseError, databaseReference);
                    }
                }
            };
    ref.removeValue(cListener);
}

privateErişim değiştiriciyi ile nasıl kullanabilirsiniz interface?
Rudra

0

Genel olarak yapıcılar, nesneye göre belirli sınıftaki statik olmayan üyeleri başlatmak içindir.

Yalnızca bildirilen yöntemler olduğu gibi tanımlanmış yöntemler olmadığı için arabirim için nesne oluşturma işlemi yoktur. Bildirilen yöntemlere neden nesne oluşturamıyoruz, nesne oluşturma, statik olmayan üyeler için bazı bellek (yığın bellekte) ayırmaktan başka bir şey değildir.

JVM, tamamen geliştirilmiş ve kullanıma hazır üyeler için bellek oluşturacaktır.Bu üyelere dayanarak, JVM onlar için ne kadar bellek gerektiğini hesaplar ve bellek oluşturur.

Bildirilen yöntemler söz konusu olduğunda, JVM bu gelecekte açıklanan uygulama için ne kadar bellek gerektiğini hesaplayamaz, çünkü uygulama gelecekte bu sefer yapılmayacaktır. bu nedenle arabirim için nesne oluşturma mümkün değildir.

Sonuç:

nesne oluşturma olmadan, statik olmayan üyeleri bir kurucu aracılığıyla başlatma şansı yoktur.Bu nedenle kurucuya bir arabirim içinde izin verilmez. (bir arabirim içinde kurucu kullanılmadığından)

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.