Как написать абстрактный класс в качестве интерфейса

#java #class #inheritance #interface #multiple-inheritance

#java #класс #наследование #интерфейс #множественное наследование

Вопрос:

Я много искал в Google ответ на этот вопрос, но я либо не знаю, что такое технический термин, либо это невозможно, поэтому простите любое невежество с моей стороны, пожалуйста.


Сначала код, затем обоснование того, почему.

 package package1;
public interface MyInterface {
    public ... method1(...);
    public ... method2(...);
    public ... method3(...);
    public ... method4(...);

    public static interface|class DEFAULT extends|implements MyInterface {
        public abstract ... method1(...);

        public ... method2(...) {
            // code
        }

        public ... method3(...) {
            // code
        }

        public abstract ... method4(...);
    }
}
  

Обычно я бы абстрагировал класс для реализации MyInterface , но в этом случае я не могу, поскольку Java (по понятным причинам) не поддерживает наследование классов от нескольких классов (хотя интерфейсы могут расширять множество других интерфейсов).

Мой (идеальный) вариант использования был бы чем-то вроде:

 package package3;
import package1.MyInterface;
import package2.SomeBaseClass;
public class MyClass extends SomeBaseClass implements MyInterface.DEFAULT {
    // other class code

    public ... method1(...) {
        // code
    }

    public ... method4(...) {
        // code
    }
}
  

Вероятно, я мог бы найти какой-нибудь обходной путь, но я хочу знать, есть ли более простой / элегантный способ сделать это, прежде чем я потрачу кучу времени на написание кода, который может в конечном итоге превзойти то, о чем я не подумал.


Редактировать: я должен был указать, что каждый из классов / интерфейсов из разных пакетов (приведенный выше код был изменен, чтобы отразить это различие). По сути, я объединяю функциональность двух пакетов в третий.


Редактировать: я придумал следующее, которое, как я полагаю, следует предложенным решениям комментариям для выполнения этого с помощью композиции.

 package MyPackage;

import package1.SomeBaseClass;
import package2.MyInterface;

public class MyClass extends SomeBaseClass {
    private .. someVariable;

    private MyInterface myInterface = new MyInterface.DEFAULT() {
        public ... method1(...) {
            // do something with someVariable
        }

        public ... method4(...) {
            // code
        }
    };

    public void doSomethingThatRequiresMyInterface() {
        myInterface.method1(...);
    }
}
  

Правильно ли я это понимаю?

Комментарии:

1. Используете ли вы Java 8? Если это так, найдите в Google «методы по умолчанию». Если нет, то то, что вы хотите, невозможно.

2. В вашем вопросе вы не объясняете, почему бы просто не использовать as abstract class, зачем вам нужна комбинация абстрактного класса и интерфейса.

3. @alfasin Я думал, что представленный идеальный вариант использования показал это.

4. @JBNizet Java 7, хотя обновление может быть полезным, но есть проблемы с совместимостью для более старых версий.

5. Java 7 не позволяет методам интерфейсов иметь реализацию. Таким образом, вы не можете реализовать DEFAULT как интерфейс. Но это также не позволяет наследовать от двух разных классов. Так что это невозможно. Java 8 позволяет интерфейсам иметь методы по умолчанию, которые обеспечивают реализацию (но все еще могут не иметь состояния). Таким образом, методы Java 8 по умолчанию могут быть ответом. Но я предполагаю, что вам следует использовать композицию, а не наследование.

Ответ №1:

Если вы используете / можете использовать Java 8, «методы по умолчанию» могут быть тем, что вы ищете -> http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

Однако я не думаю, что вы далеки от того, чего хотите достичь с помощью кода, указанного выше.

MyInterface.java

     public interface MyInterface {
        public ... method1(...);
        public ... method2(...);
        public ... method3(...);
        public ... method4(...);
    }
  

По умолчанию.java

 public abstract class Default implements MyInterface {
    public abstract ... method1(...);

    public ... method2(...) {
        // code
    }

    public ... method3(...) {
        // code
    }

    public abstract ... method4(...);
}
  

SomeBaseClass.java

 public abstract class SomeBaseClass extends Default {
    public void someNewMethod();
}
  

MyClass.java

 public class MyClass extends SomeBaseClass {
    // other class code

    @Override
    public ... method1(...) {
        // code
    }

    @Override
    public ... method4(...) {
        // code
    }
}
  

Комментарии:

1. Я отредактировал вопрос, чтобы включить информацию о пакете импорта.

Ответ №2:

Если я правильно понял ваши требования, я думаю, что новые методы по умолчанию (также известные как методы defender), которые были введены в Java 8, могли бы вам помочь (учебное пособие в Oracle).

Они позволяют определять поведение по умолчанию для методов интерфейса, так что реализующим их классам не нужно дублировать это поведение по умолчанию, если этого достаточно.

Это было введено главным образом для того, чтобы команда разработчиков Java могла добавлять новые методы в интерфейсы API, не нарушая существующий код, реализующий эти интерфейсы.

Пример :

Интерфейс :

 public interface MyInterface {
    default void method1(String arg1, Object arg2){
        //code...
    }

    default String method2(){
        //code...
    }

    String method3();
}
  

Класс реализации :

 public class MyClass implements MyInterface {

    private String field1;

    public String method3(){
        //code...
    }

    public void someOtherMethod(){
        //code...
    }
}
  

В этом случае класс реализации не реализует методы интерфейса, помеченные как стандартные, поэтому их все еще можно вызвать и будет использоваться реализация по умолчанию. Однако он все еще должен реализовать method3 , который не имеет реализации по умолчанию в интерфейсе.

Однако существуют ограничения и предостережения, некоторые из которых перечислены в этой статье в блоге, поэтому будьте осторожны.

В качестве отступления: вам не нужно указывать «общедоступный» метод доступа для методов интерфейсов в Java, поскольку он уже используется по умолчанию,

Комментарии:

1. Я помещаю «общедоступный» аксессор в интерфейсы, чтобы я мог выполнять прямое копирование и вставку из интерфейса в реализующий класс, поэтому я не забываю указывать аксессор, что упрощает, если я не использую Eclipse во время выполнения этого конкретного сеанса кодирования (что часто бывает).

Ответ №3:

Вы могли бы использовать композицию, где вы бы создали класс, который реализует интерфейс и предоставляет функциональность по умолчанию, а затем передали экземпляр реализации по умолчанию конкретному классу. Если конкретный класс не предоставляет функциональность, вызовите версию, реализованную экземпляром по умолчанию.

Интерфейс:

 public interface Foo {

    int method1(int a, String b);

    String method2(int a);

    String method3(String b);

}
  

Реализация по умолчанию:

 public class DefaultFoo implements Foo {

    @Override
    public int method1(int a, String b) {
        return a   b.length();
    }

    @Override
    public String method2(int a) {
        return a;
    }

    @Override
    public String method3(String b) {
        return b;
    }

}
  

Конкретная реализация:

 public class GreenFoo implements Foo {

    Foo defaultFoo = new DefaultFoo();

    @Override
    public int method1(int a, String b) {
        return "Green "   a   ", Mean"   b;
    }

    @Override
    public int method2(int a) {
        return a*a;
    }

    @Override
    public int method3(Sting b) {
        // I don't want to implement this, so I just call the default.
        return defaultFoo.method3(b);
    }

}