Java - Arayüz uygulamasında yöntem adı çakışması


88

Her ikisi de amaçları açısından oldukça farklı, ancak aynı yöntem imzasına sahip iki arabirimim varsa, hem arabirime hizmet eden tek bir yöntem yazmaya hem de yöntemde bazı kıvrımlı mantık yazmaya zorlanmadan bir sınıfı nasıl uygulayabilirim? Çağrının hangi tür nesne için yapıldığını kontrol eden ve uygun kodu çağıran uygulama?

C # 'da, bu, açık arabirim uygulaması olarak adlandırılan şeyle aşılır. Java'da eşdeğer bir yol var mı?


37
Ne zaman bir sınıf yapmak aynı imzaya sahip iki yöntem uygulamak zorundadır farklı şeyler sonra sınıf olup, neredeyse kesinlikle çok şey yapıyor.
Joachim Sauer

15
Yukarıdakiler her zaman doğru olmayabilir IMO. Bazen, tek bir sınıfta, harici bir sözleşmeyi onaylaması gereken (dolayısıyla imzaları kısıtlayan), ancak farklı uygulamaları olan yöntemlere ihtiyacınız vardır. Aslında, bunlar önemsiz olmayan bir sınıf tasarlarken ortak gereksinimlerdir. Aşırı yükleme ve geçersiz kılma, imzada farklılık göstermeyen veya çok az farklılık gösteren farklı şeyler yapan yöntemlere izin veren mekanizmalardır. Burada sahip olduğum şey, alt sınıflamaya izin vermemesi / imzalarda en ufak bir değişiklik.
Bhaskar

1
Bu sınıfların ve yöntemlerin ne olduğunu bilmek ilgimi çeker.
Uri

2
Eski bir "Adres" sınıfının bir getName () yöntemine sahip olan Kişi ve Firma arabirimlerini uyguladığı ve yalnızca veri modelinden bir Dize döndürdüğü böyle bir durumla karşılaştım. Person.getName () öğesinin "Soyadı, Verilen adlar" olarak biçimlendirilmiş bir Dize döndürdüğünü belirten yeni bir iş gereksinimi. Uzun tartışmalardan sonra, veriler bunun yerine veri tabanında yeniden biçimlendirildi.
belwood

12
Sadece sınıfın neredeyse kesinlikle çok fazla şey yaptığını belirtmek YAPICI DEĞİLDİR. Şu anda, sınıfımın 2 farklı arayüzden mehod adı çarpışmaları olduğu ve sınıfım çok fazla şey YAPMADIĞINDA tam da bu durumu anladım. Amaçlar oldukça benzer, ancak biraz farklı şeyler yapın. Soruyu soran kişiyi kötü yazılım tasarımı uygulamakla suçlayarak bariz bir şekilde ciddi şekilde engellenmiş bir programlama dilini savunmaya çalışmayın!
j00hi

Yanıtlar:


75

Hayır, aynı yöntemi Java'da bir sınıfta iki farklı şekilde uygulamanın bir yolu yoktur.

Bu birçok kafa karıştırıcı duruma yol açabilir, bu yüzden Java buna izin vermedi.

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

class Impl implements ISomething, ISomething2 {
   void doSomething() {} // There can only be one implementation of this method.
}

Yapabileceğiniz şey, her biri farklı bir arabirim uygulayan iki sınıftan bir sınıf oluşturmaktır. O zaman bu sınıf, her iki arayüzün davranışına sahip olacaktır.

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}

9
Ancak bu şekilde, bir CompositeClass örneğini arabirimlerin bir referansı (ISomething veya ISomething2) beklenen bir yere geçiremem? İstemci kodunun örneğimi uygun arayüze aktarmasını bile bekleyemiyorum, bu yüzden bu kısıtlamayla bir şey kaybetmeyecek miyim? Ayrıca, bu şekilde, ilgili arayüzleri fiilen uygulayan sınıflar yazarken, kodu tek bir sınıfa sahip olmanın avantajını kaybettiğimizi ve bu durumun bazen ciddi bir engel olabileceğini unutmayın.
Bhaskar

9
@Bhaskar, geçerli puanlar veriyorsun. Sahip olduğum en iyi tavsiye , o sınıfa bir ISomething1 CompositeClass.asInterface1();ve ISomething2 CompositeClass.asInterface2();yöntem eklemektir . O zaman kompozit sınıftan birini veya diğerini alabilirsiniz. Yine de bu sorunun harika bir çözümü yok.
jjnguy

1
Bunun yol açabileceği kafa karıştırıcı durumlardan bahsetmişken, bir örnek verebilir misiniz? Yöntem adına eklenen arayüz adını, daha sonra çarpışmayı / karışıklığı önleyebilecek ekstra bir kapsam çözümü olarak düşünemez miyiz?
Bhaskar

@Bhaskar Sınıflarımızın tek sorumluluk ilkesine bağlı kalması daha iyi. Çok farklı iki arayüzü uygulayan bir sınıf varsa, tek sorumluluğu yerine getirmek için sınıfları bölmek için tasarımın yeniden çalışılması gerektiğini düşünüyorum.
Anirudhan J

1
public long getCountAsLong() implements interface2.getCount {...}[Arayüzün bir gerektirdiği longancak sınıftaki kullanıcıların beklemesi durumunda int] veya private void AddStub(T newObj) implements coolectionInterface.Add[ collectionInterfacebir canAdd()metoda sahip olduğunu ve bu sınıfın tüm örnekleri için geri döndüğünü varsayarak ] gibi bir şeye izin vermek gerçekten ne kadar kafa karıştırıcı olurdu false?
supercat

13

Bunu Java'da çözmenin gerçek bir yolu yok. İç sınıfları geçici çözüm olarak kullanabilirsiniz:

interface Alfa { void m(); }
interface Beta { void m(); }
class AlfaBeta implements Alfa {
    private int value;
    public void m() { ++value; } // Alfa.m()
    public Beta asBeta() {
        return new Beta(){
            public void m() { --value; } // Beta.m()
        };
    }
}

Ondan atmalarını için izin vermemekle birlikte AlfaBetaiçin Beta, downcasts genellikle kötülük vardır ve bir o beklenebilir eğer Alfaörneği genellikle vardır Betasen isterler de yönünü ve nedense (genellikle optimizasyon geçerlidir nedenidir) dönüştürmek için Beta, size bir alt arayüzü yapabilir Alfaile Beta asBeta()onun içinde.


İç sınıftan ziyade anonim sınıfı mı kastediyorsunuz?
Zaid Masud

2
@ZaidMasud İç sınıfları kastediyorum, çünkü çevreleyen nesnenin özel durumuna erişebilirler). Bu iç sınıflar da elbette anonim olabilir.
gustafc

11

Bu sorunla karşılaşıyorsanız, bunun nedeni büyük olasılıkla temsilci kullanmanız gereken yerde kalıtımı kullanmanızdır . Aynı temel veri modeli için benzer de olsa iki farklı arabirim sağlamanız gerekiyorsa, bir görünüm kullanmalısınız sağlamanız gerekiyorsa, başka bir arabirimi kullanarak verilere ucuza erişim sağlamak kullanmalısınız.

İkinci durumda yönelik somut bir örnek vermek gerekirse, her iki uygulamak istediğinizi varsayalım Collectionve MyCollection(devralan olmadığı Collectionve uyumsuz bir arayüze sahip). Aynı temel verileri kullanan ve hafif bir uygulama sağlayan a Collection getCollectionView()ve MyCollection getMyCollectionView()işlevleri sağlayabilirsiniz .CollectionMyCollection

İlk durum için ... farz edin ki gerçekten bir tamsayı dizisi ve bir dizi dizge istiyorsunuz. Bunun yerine hem devralmak List<Integer>ve List<String>, sen bir tür üyesi olmalıdır List<Integer>ve türde başka üyesi List<String>ve hem devralır ziyade denemede daha bu üyeler bakın. Yalnızca tam sayıların bir listesine ihtiyacınız olsa bile, bu durumda miras yerine kompozisyon / yetkilendirmeyi kullanmak daha iyidir.


Ben öyle düşünmüyorum. Onlarla uyumlu olmak için farklı arayüzler uygulamanızı gerektiren kitaplıkları unutuyorsunuz. Birden çok çakışan kitaplığı kullanarak bununla çok daha sık karşılaşabilirsiniz, ardından kendi kodunuzda bununla karşılaşırsınız.
nightpool

1
@nightpool, her biri farklı arabirimler gerektiren birden çok kitaplık kullanıyorsanız, yine de tek bir nesnenin her iki arabirimi de uygulaması gerekmez; nesnenin iki farklı arabirimin her birini döndürmek için erişimcilere sahip olmasını sağlayabilirsiniz (ve nesne boyunca temel kitaplıklardan birine geçerken uygun erişimciyi çağırabilirsiniz).
Michael Aaron Safyan

1

"Klasik" Java problemi Android geliştirmemi de etkiliyor ...
Nedeni basit görünüyor:
Daha fazla çerçeve / kitaplık kullanmanız gerek, daha kolay işler kontrolden çıkabilir ...

Benim durumumda bir BootStrapperApp sınıfım var android.app.Application'dan miras alınır ,
oysa aynı sınıf, entegre olmak için bir MVVM çerçevesinin Platform arayüzünü de uygulamalıdır . Her iki arabirim tarafından bildirilen ve farklı bağlamlarda farklı uygulamalara sahip olması gereken getString () yönteminde
yöntem çakışması meydana geldi .
Çözüm (çirkin..IMO), tüm Platformu uygulamak için bir iç sınıf kullanıyoryöntemler, sadece küçük bir yöntem imza çakışması nedeniyle ... bazı durumlarda, bu tür ödünç alınan yöntem hiç kullanılmaz (ancak büyük tasarım anlamlarını etkiler).
C # tarzı açık bağlam / ad alanı göstergesinin yardımcı olduğunu kabul ediyorum.


1
Android geliştirme için Java kullanmaya başlayana kadar C # 'ın ne kadar düşünceli ve zengin özelliklere sahip olduğunu hiç anlamadım. Bu C # özelliklerini kesin olarak aldım. Java çok fazla özellikten yoksundur.
Damn Vegetables

1

Aklıma gelen tek çözüm, birden fazla arayüz yerleştirmek istediğiniz nesneye yönlendirme nesneleri kullanmaktır.

Ör: uygulamak için 2 arayüzünüz olduğunu varsayalım

public interface Framework1Interface {

    void method(Object o);
}

ve

public interface Framework2Interface {
    void method(Object o);
}

bunları iki Facador nesnesine dahil edebilirsiniz:

public class Facador1 implements Framework1Interface {

    private final ObjectToUse reference;

    public static Framework1Interface Create(ObjectToUse ref) {
        return new Facador1(ref);
    }

    private Facador1(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework1Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork1(o);
    }
}

ve

public class Facador2 implements Framework2Interface {

    private final ObjectToUse reference;

    public static Framework2Interface Create(ObjectToUse ref) {
        return new Facador2(ref);
    }

    private Facador2(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework2Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork2(o);
    }
}

Sonunda istediğin ders şöyle olmalı

public class ObjectToUse {

    private Framework1Interface facFramework1Interface;
    private Framework2Interface facFramework2Interface;

    public ObjectToUse() {
    }

    public Framework1Interface getAsFramework1Interface() {
        if (facFramework1Interface == null) {
            facFramework1Interface = Facador1.Create(this);
        }
        return facFramework1Interface;
    }

    public Framework2Interface getAsFramework2Interface() {
        if (facFramework2Interface == null) {
            facFramework2Interface = Facador2.Create(this);
        }
        return facFramework2Interface;
    }

    public void methodForFrameWork1(Object o) {
    }

    public void methodForFrameWork2(Object o) {
    }
}

artık sınıfınızı "göstermek" için getAs * yöntemlerini kullanabilirsiniz


0

Bunların çalışmasını sağlamak için bir Adaptör modeli kullanabilirsiniz. Her arayüz için iki adaptör oluşturun ve bunu kullanın. Sorunu çözmeli.


-1

Söz konusu kodun tamamı üzerinde tam kontrole sahip olduğunuzda ve bunu önceden uygulayabildiğinizde, her şey yolunda. Şimdi birçok yerde bir yöntemle kullanılan mevcut bir genel sınıfınız olduğunu hayal edin

public class MyClass{

    private String name;

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

Şimdi bunu, sınıfların aynı zamanda bir getName () yöntemine sahip olan WBPInterface ... uygulamasını gerektiren kullanıma hazır WizzBangProcessor'a aktarmanız gerekir, ancak somut uygulamanız yerine, bu arabirim yöntemin bir türün adını döndürmesini bekler. of Wizz Bang Processing.

C # 'da bir trvial olurdu

public class MyClass : WBPInterface{

    private String name;

    String WBPInterface.getName(){
        return "MyWizzBangProcessor";
    }

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

Java Tough'da, bir arabirimden diğerine dönüştürmeniz gereken mevcut konuşlandırılmış kod tabanındaki her noktayı tanımlamanız gerekecek. Elbette WizzBangProcessor şirketi getWizzBangProcessName () kullanmalıydı, ancak onlar da geliştiriciler. Kendi bağlamında getName iyiydi. Aslında, Java dışında, diğer OO tabanlı dillerin çoğu bunu destekler. Java, tüm arabirimleri aynı NAME yöntemiyle uygulanmaya zorlamada nadirdir.

Diğer dillerin çoğunda, "bu uygulanan arayüzde bu yöntemin imzasıyla eşleşen bu sınıftaki bu yöntem, uygulanmasıdır" diyen bir talimat almaktan çok mutlu olan bir derleyiciye sahiptir. Sonuçta, arayüzleri tanımlamanın tüm amacı, tanımın uygulamadan soyutlanmasına izin vermektir. (Beni Java Arayüzlerinde varsayılan yöntemlere sahip olmaya bile başlatmayın, varsayılanı geçersiz kılmayı bırakın ... çünkü tabii ki, bir yol arabası için tasarlanan her bileşen uçan bir arabaya çarpıp sadece çalışabilmelidir - hey ikisi de araba ... eminim ki, uydu navigasyonunuzun varsayılan işlevselliği, varsayılan perde ve yuvarlanma girişlerinden etkilenmeyecektir, çünkü arabalar sadece yalpalama!

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.