Если у меня есть два интерфейса, оба совершенно разных по своему назначению, но с одинаковой сигнатурой метода, как мне сделать класс, реализующий оба, без необходимости писать один метод, который служит для обоих интерфейсов, и писать некоторую запутанную логику в методе реализация, которая проверяет, для какого типа объекта выполняется вызов, и вызывает правильный код?
В C # это преодолевается тем, что называется явной реализацией интерфейса. Есть ли эквивалентный способ в Java?
Ответы:
Нет, в Java невозможно реализовать один и тот же метод двумя разными способами в одном классе.
Это может привести к множеству запутанных ситуаций, поэтому Java не разрешает это.
interface ISomething { void doSomething(); } interface ISomething2 { void doSomething(); } class Impl implements ISomething, ISomething2 { void doSomething() {} // There can only be one implementation of this method. }
Что вы можете сделать, так это составить класс из двух классов, каждый из которых реализует свой интерфейс. Тогда этот один класс будет иметь поведение обоих интерфейсов.
class CompositeClass { ISomething class1; ISomething2 class2; void doSomething1(){class1.doSomething();} void doSomething2(){class2.doSomething();} }
источник
ISomething1 CompositeClass.asInterface1();
иISomething2 CompositeClass.asInterface2();
метод этого класса. Тогда вы можете просто получить тот или иной из составного класса. Однако нет отличного решения этой проблемы.public long getCountAsLong() implements interface2.getCount {...}
[в случае, если интерфейс требует a,long
но пользователи этого класса ожидаютint
] илиprivate void AddStub(T newObj) implements coolectionInterface.Add
[предполагая, что уcollectionInterface
него естьcanAdd()
метод, и для всех экземпляров этого класса он возвращаетfalse
]?На Java нет реального способа решить эту проблему. Вы можете использовать внутренние классы как обходной путь:
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() }; } }
Несмотря на то, что он не допускает приведений из
AlfaBeta
вBeta
, отрицательные преобразования обычно являются злом, и если можно ожидать, чтоAlfa
экземпляр также часто имеетBeta
аспект, и по какой-то причине (обычно оптимизация является единственной действительной причиной) вы хотите иметь возможность чтобы преобразовать его вBeta
, вы можете сделать в нем субинтерфейсAlfa
withBeta asBeta()
.источник
Если вы столкнулись с этой проблемой, скорее всего, вы используете наследование там, где вам следует использовать делегирование . Если вам нужно предоставить два разных, хотя и похожих, интерфейса для одной и той же базовой модели данных, тогда вам следует использовать представление, чтобы дешево предоставить доступ к данным с помощью другого интерфейса.
Чтобы дать конкретный пример для последнего случая, предположим, что вы хотите реализовать оба
Collection
иMyCollection
(которые не наследуютсяCollection
и имеют несовместимый интерфейс). Можно обеспечитьCollection getCollectionView()
иMyCollection getMyCollectionView()
функции , которые обеспечивают реализацию светового весаCollection
иMyCollection
, используя те же данные.В первом случае ... предположим, что вам действительно нужен массив целых чисел и массив строк. Вместо наследования от обоих
List<Integer>
иList<String>
вы должны иметь один член типаList<Integer>
и другой член типаList<String>
и ссылаться на эти члены, а не пытаться наследовать от обоих. Даже если вам нужен только список целых чисел, в этом случае лучше использовать композицию / делегирование вместо наследования.источник
"Классическая" проблема Java также влияет на мою разработку под Android ...
Причина, кажется, проста:
больше фреймворков / библиотек, которые вы должны использовать, легче выходит из-под контроля ...
В моем случае у меня есть класс BootStrapperApp унаследован от android.app.Application , в
то время как тот же класс должен также реализовать интерфейс платформы платформы MVVM для интеграции.
Конфликт методов произошел в методе getString () , который объявляется обоими интерфейсами и должен иметь разные реализации в разных контекстах.
Обходной путь (уродливый .. IMO) использует внутренний класс для реализации всех платформметоды, просто из-за одного незначительного конфликта сигнатур метода ... в некоторых случаях такой заимствованный метод вообще не используется (но влияет на основную семантику проекта).
Я склонен согласиться с тем, что явное указание контекста / пространства имен в стиле C # полезно.
источник
Единственное решение, которое пришло мне в голову, - использовать объекты ссылки на тот, который вы хотите реализовать с несколькими интерфейсами.
например: предположим, что у вас есть 2 интерфейса для реализации
public interface Framework1Interface { void method(Object o); }
а также
public interface Framework2Interface { void method(Object o); }
вы можете заключить их в два объекта Facador:
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); } }
а также
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); } }
В конце концов, класс, который вы хотели, должен был выглядеть примерно так:
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) { } }
теперь вы можете использовать методы getAs *, чтобы «раскрыть» свой класс
источник
Вы можете использовать шаблон адаптера, чтобы все заработало. Создайте два адаптера для каждого интерфейса и используйте их. Это должно решить проблему.
источник
Все хорошо, если у вас есть полный контроль над всем рассматриваемым кодом и вы можете реализовать это заранее. Теперь представьте, что у вас есть существующий общедоступный класс, который используется во многих местах с методом
public class MyClass{ private String name; MyClass(String name){ this.name = name; } public String getName(){ return name; } }
Теперь вам нужно передать его в готовый WizzBangProcessor, которому требуются классы для реализации WBPInterface ... который также имеет метод getName (), но вместо вашей конкретной реализации этот интерфейс ожидает, что метод вернет имя типа компании Wizz Bang Processing.
В C # это будет тривиал
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 вам нужно будет идентифицировать каждую точку в существующей развернутой базе кода, где вам нужно преобразовать один интерфейс в другой. Конечно, компании WizzBangProcessor следовало использовать getWizzBangProcessName (), но они тоже разработчики. В их контексте getName было нормально. Фактически, за пределами Java, большинство других языков, основанных на объектно-ориентированных технологиях, поддерживают это. Java редко заставляет реализовывать все интерфейсы одним и тем же методом NAME.
В большинстве других языков есть компилятор, который более чем счастлив принять инструкцию, чтобы сказать: «этот метод в этом классе, который соответствует сигнатуре этого метода в этом реализованном интерфейсе, является его реализацией». В конце концов, весь смысл определения интерфейсов состоит в том, чтобы позволить абстрагировать определение от реализации. (Даже не заставляйте меня использовать методы по умолчанию в интерфейсах в Java, не говоря уже о переопределении по умолчанию .... потому что, конечно, каждый компонент, разработанный для дорожного автомобиля, должен иметь возможность врезаться в летающий автомобиль и просто работать - эй они обе машины ... Я уверен, что на функции по умолчанию, скажем, вашу спутниковую навигацию не повлияют входные данные по тангажу и крену, потому что машины только рыскают!
источник