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

Что такое двоичная совместимость в Java?

Я читал Эффективная Java от Джошуа Блоха.

В пункте 17: "Использовать интерфейсы только для определения типов", я наткнулся на объяснение, где не рекомендуется использовать интерфейсы для хранения констант. Я объясняю ниже.

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

Что означает здесь двоичная совместимость?

Может ли кто-нибудь привести меня с примером в Java, чтобы показать, что код совместим с бинарными.

4b9b3361

Ответ 1

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

public class Logger implements Constants {
   public Logger getLogger(String name) {
         return LogManager.getLogger(name);
   }
}

из вашей библиотеки log-1.jar и выпустил новую версию как log-2.jar. Когда пользователи вашего log-1.jar загружают новую версию, она сломает свои приложения, когда они попытаются использовать отсутствующий метод getLogger (String name).

И если вы удалите интерфейс Constants (пункт 17), это приведет к потере бинарной совместимости по той же причине.

Но вы можете удалить/переименовать частный или пакетный частный член этого класса, не нарушая бинарную совместимость, поскольку внешние приложения не могут (или не должны) использовать его.

Ответ 2

Чтобы лучше понять концепцию, интересно видеть, что двоичная совместимость НЕ подразумевает совместимость API, и наоборот.

совместимый с API, но не совместимый с бинарным: статическое удаление

Версия 1 библиотеки:

public class Lib {
    public static final int i = 1;
}

Клиентский код:

public class Main {
    public static void main(String[] args) {
        if ((new Lib()).i != 1) throw null;
    }
}

Скомпилируйте клиентский код с версией 1:

javac Main.java

Замените версию 1 на версию 2: удалите static:

public class Lib {
    public final int i = 1;
}

Перекомпилируйте только версию 2, а не код клиента, и запустите java Main:

javac Lib.java
java Main

Получаем:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field Lib.i
        at Main.main(Main.java:3)

Это происходит потому, что, хотя мы можем писать (new Lib()).i в Java для методов static и member, он компилируется в две разные команды VM в зависимости от Lib: getstatic или getfield. Этот перерыв упоминается в JLS 7 13.4.10:

Если поле, которое не объявлено приватным, не было объявлено статическим и было изменено так, чтобы оно было объявлено статическим, или наоборот, тогда возникает ошибка привязки, в частности, IncompatibleClassChangeError, если поле используется существующим двоичным кодом, ожидал поле другого рода.

Нам понадобится перекомпилировать Main с javac Main.java, чтобы он работал с новой версией.

Примечания:

  • вызов статических членов из экземпляров класса, таких как (new Lib()).i, является плохим стилем, вызывает предупреждение и никогда не должен выполняться
  • этот пример надуман, потому что нестатические примитивы final бесполезны: всегда используйте static final для примитивов: закрытый конечный статический атрибут vs private final attribute
  • отражение можно использовать чтобы увидеть разницу. Но отражение также может видеть частные поля, что, очевидно, приводит к разрыву, который не должен считаться перерывами, поэтому он не учитывается.

совместимый с двоичными файлами, но не совместимый с API: нулевое предварительное условие

Версия 1:

public class Lib {
    /** o can be null */
    public static void method(Object o) {
        if (o != null) o.hashCode();
    }
}

Версия 2:

public class Lib {
    /** o cannot be null */
    public static void method(Object o) {
        o.hashCode();
    }
}

Клиент:

public class Main {
    public static void main(String[] args) {
        Lib.method(null);
    }
}

На этот раз, даже если перекомпилировать Main после обновления Lib, второй вызов будет вызывать, но не первый.

Это связано с тем, что мы сменили контракт method таким образом, который нельзя проверить во время компиляции Java: прежде чем он сможет принять null, больше не будет.

Примечания:

  • Wiki для Eclipse - отличный источник для этого: https://wiki.eclipse.org/Evolving_Java-based_APIs
  • создание API, которые принимают значения null, является сомнительной практикой.
  • гораздо проще сделать изменение, которое нарушает совместимость API, но не двоично, чем наоборот, поскольку легко изменить внутреннюю логику методов.

Ответ 3

Двоичная совместимость

Java-двоичная совместимость предписывает условия какая модификация и повторная компиляция классов не требует перекомпиляции дополнительных классов импорта- в модифицированных классах. Бинарная совместимость - это роман Концепция языкового дизайна.

Спецификация языка Java [7] описывает двоичную компиляцию, патч изменяется следующим образом:

Изменение типа бинарно совместимо с (эквивалентно, не нарушает совместимость с) ранее существовавшие двоичные файлы, если ранее существовавшие двоичные файлы которые ранее были связаны без ошибок, tinue для ссылки без ошибок.

Ответ 4

Если в будущем мы хотим изменить интерфейс, который реализуют некоторые классы (например, добавление некоторых новых методов).

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

Чтобы преодолеть это, мы можем добавить в интерфейс стандартные методы.

Это приведет к удалению зависимости для реализации дополнительных методов.

Нам не нужно изменять класс реализации для включения изменений. Это называется бинарной совместимостью.

Пожалуйста, обратитесь к приведенному ниже примеру:

Интерфейс, который мы будем использовать

    //Interface       
    interface SampleInterface
            {
                // abstract method
                public void abstractMethod(int side);

                // default method
                default void defaultMethod() {
                   System.out.println("Default Method Block");
                }

                // static method
                static void staticMethod() {
                    System.out.println("Static Method Block");
                }
            }


//The Class that implements the above interface.

    class SampleClass implements SampleInterface
    {
        /* implementation of abstractMethod abstract method, if not implemented 
        will throw compiler error. */
        public void abstractMethod(int side)
        {System.out.println(side*side);}

        public static void main(String args[])
        {
            SampleClass sc = new SampleClass();
            sc.abstractMethod(4);

            // default method executed
            sc.defaultMethod();

            // Static method executed
            SampleInterface.staticMethod();

        }
    }

Примечание.. Для получения более подробной информации см. методы по умолчанию

Ответ 5

Чтобы все выглядело просто:

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

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