Окончательный интерфейс в Java? - PullRequest
31 голосов
/ 04 июня 2010

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

Ответы [ 10 ]

22 голосов
/ 04 июня 2010

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

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

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

14 голосов
/ 04 июня 2010

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

11 голосов
/ 04 июня 2010

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

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

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

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

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

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

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

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

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

    Очевидно, что это полностью противоречит цели интерфейса.

8 голосов
/ 04 июня 2010

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

9.1.1.1 абстрактные интерфейсы

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

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

4 голосов
/ 04 июня 2010

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

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

    интерфейс FinalInterface { }

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

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

    _cg = new ClassGen ("FinalInterface", "java.lang.Object", "FinalInterface.java", ACC_INTERFACE | ACC_ABSTRACT | ACC_FINAL, новая строка [] {});

  4. Скомпилируйте и запустите отредактированный файл 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, но зачем вам это нужно?

2 голосов
/ 04 июня 2010

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

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

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

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

1 голос
/ 13 августа 2015

При разработке API в Java Иногда я нахожу ситуацию, когда типом в API должен быть Java interface , но число его реализаций должно быть ограничено - например, только внутренняя часть 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 вызывает ваш процессор аннотаций и позволяет вам завершить компиляцию, если обнаружит, что кто-то другой пытается реализовать ваш интерфейс.

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

0 голосов
/ 23 февраля 2018

final означает, что его нельзя изменить. Если это класс, он не может быть изменен расширением (методы могут быть изменены путем переопределения, атрибуты не могут быть изменены; к добавленным атрибутам / методам можно получить доступ только как к членам «типа подкласса»); если это переменная (attribute / local), то значение не может быть изменено после первого присваивания; если это метод, реализация не может быть изменена (конечный метод может быть перегружен, но не может быть переопределен). Нет смысла объявлять интерфейс финальным, так как обычно нет никакой реализации для изменения (методы по умолчанию и статические методы интерфейса не могут быть финальными).

0 голосов
/ 04 июня 2010

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

0 голосов
/ 04 июня 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...