Подтвердить что ты не робот

Заключительный интерфейс в Java?

Может ли интерфейс быть объявлен как final в Java?

4b9b3361

Ответ 1

Интерфейсы являются абстрактными 100%, и единственный способ создать экземпляр интерфейса - создать экземпляр класса, который его реализует. Разрешение интерфейсов final совершенно бессмысленно.

РЕДАКТИРОВАТЬ Вопросы не так безобразны, как я думал сначала. Конечный интерфейс - это тот, который не может быть расширен другими интерфейсами, но может быть реализован, как представляется, имеет смысл.

Я мог бы подумать об одном различии между окончательным классом и окончательным интерфейсом. Расширение класса может скомпрометировать его целостность, поскольку оно содержит некоторое состояние. Расширение интерфейса просто добавляет операции и не может скомпрометировать целостность реализации, потому что интерфейс сам по себе не имеет места.

Ответ 2

Нет. Попытка объявить интерфейс как окончательный в Java приводит к ошибке компиляции. Это решение для языкового дизайна - интерфейсы Java предназначены для расширения.

Ответ 3

Нет. Раздел "Спецификация языка Java" 9.1.1. Модификаторы интерфейса сообщают следующее:

Объявление интерфейса может включать модификаторы интерфейса.

InterfaceModifier:
  (one of)
  Annotation public protected private
  abstract static strictfp

Как видно, список не включает final.

Почему язык был разработан таким образом?

Если интерфейс был объявлен final, я полагаю, это могло означать, что

  • Никакой другой интерфейс не может его расширить

    Это было бы нечувствительным ограничением. Причинами, по которым полезно объявить окончательный класс, является защита инвариантов состояния, запрещение переопределения всех методов одновременно и т.д. Ни одно из этих ограничений не имеет смысла для интерфейсов. (Нет состояния, и все методы должны быть переопределены.)

  • Ни один класс не может реализовать интерфейс

    Это явно полностью лишает цель интерфейса.

Ответ 4

Из спецификации языка Java (третье издание):

9.1.1.1 аннотация Интерфейсы

Каждый интерфейс неявно abstract. Этот модификатор устарел и не должны использоваться в новых программы.

Итак, abstract + final является своего рода оксюмороном.

Ответ 5

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

  • Скомпилируйте не финальный интерфейс. Я сохранил приведенный ниже код в FinalInterface.java. Затем я скомпилировал его.

    Интерфейс

    FinalInterface { }

  • Запустите BCELifier. Это создало файл FinalInterfaceCreator.java

  • Отредактируйте его. Найдите строку, подобную приведенной ниже, и добавьте ACC_FINAL.

    _cg = new ClassGen ( "FinalInterface", "java.lang.Object", "FinalInterface.java", ACC_INTERFACE | ACC_ABSTRACT | ACC_FINAL, новый String [] {});

  • Скомпилируйте и запустите отредактированный файл FinalInterfaceCreator.java. Это должно перезаписать исходный файл FinalInterface.class новым, похожим, но окончательным.

Чтобы протестировать его, я создал два новых Java файла TestInterface и TestClass. TestInterface - это интерфейс, который расширяет FinalInterface, а TestClass - это класс, который реализует FinalInterface. Компилятор отказался компилировать либо потому, что FinalInterface является окончательным.

TestClass.java:2: cannot inherit from final FinalInterface
class TestClass implements FinalInterface

TestInterface.java:2: cannot inherit from final FinalInterface
interface TestInterface extends FinalInterface

Кроме того, я попытался создать экземпляр FinalInterface с помощью динамических прокси-серверов

class Main
{
    public static void main ( String [ ] args )
    {
    Class < ? > ntrfc = FinalInterface . class ;
    ClassLoader classLoader = ntrfc . getClassLoader ( ) ;
    Class < ? > [ ] interfaces = { ntrfc } ;
    java . lang . reflect . InvocationHandler invocationHandler = new java . lang . reflect . InvocationHandler ( )
        {
        public Object invoke ( Object proxy , java . lang . reflect . Method method , Object [ ] args )
        {
            return ( null ) ;
        }
        } ;
    FinalInterface fi = ( FinalInterface ) ( java . lang . reflect . Proxy . newProxyInstance ( classLoader , interfaces , invocationHandler ) ) ;
    }
}

Этот скомпилированный, но не запущенный

Exception in thread "main" java.lang.ClassFormatError: Illegal class modifiers in class FinalInterface: 0x610
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:632)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:319)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:264)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:332)
at Main.main(Main.java:6)

Итак, данные свидетельствуют о том, что вы можете создать окончательный интерфейс в java, но зачем вам это нужно?

Ответ 6

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

Окончательный интерфейс можно использовать для

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

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

Ответ 7

При разработке API в Java я иногда нахожу ситуацию, когда тип API должен быть Java интерфейсом, но количество его реализаций должно быть ограничено - например. только внутренние интерфейсы API могут реализовать его, а не другие.

Я понял, что теперь могу (начиная с Java 1.6) ограничивать, кто реализует интерфейс с помощью обработчиков аннотаций (статья на apidesign.org), Это код:

package org.apidesign.demo.finalinterface;

import java.util.Collection;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = Processor.class)
@SupportedAnnotationTypes("*")
public final class FinalEnforcingProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        checkForViolations(roundEnv.getRootElements());
        return true;
    }

    private void checkForViolations(Collection<? extends Element> all) {
        for (Element e : all) {
            if (e instanceof TypeElement) {
                TypeElement te = (TypeElement) e;
/* exception for the only known implementation:
if ("org.apidesign.demo.finalinterface.AllowedImplementationTest".equals(
  te.getQualifiedName().toString())
) continue;
*/
                for (TypeMirror m : te.getInterfaces()) {
                    if (FinalInterface.class.getName().equals(m.toString())) {
                        processingEnv.getMessager().printMessage(
                          Diagnostic.Kind.ERROR, "Cannot implement FinalInterface", e
                        );
                    }
                }
            }
            checkForViolations(e.getEnclosedElements());
        }
    }
 }

Каждый раз, когда кто-то включает ваш JAR API в classpath и пытается скомпилировать его, компилятор Javac вызывает ваш обработчик аннотаций и позволяет вам сбой компиляции, если он обнаруживает, что кто-то еще пытается реализовать ваш интерфейс.

Я по-прежнему предпочитаю использовать окончательный класс для Client API - например. API, разрешенному только для других, но когда это невозможно, трюк с процессором аннотации является приемлемой альтернативой.

Ответ 8

Вместо объявления интерфейса в качестве финала, мы можем избежать создания объекта интерфейса.

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

Если есть какие-либо другие предложения, любезно сообщите нам

Ответ 9

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

Ответ 10

Интерфейс, который не имеет члена, известен как маркер или помеченный интерфейс. Например: Serializable, Cloneable, Remote и т.д. Они используются для предоставления некоторой важной информации JVM, чтобы JVM могла выполнить некоторую полезную операцию.