0

Разрушают ли стандартные методы Java 8 совместимость исходного кода?

11

Описание проблемы:

В большинстве случаев исходный код на Java был обратно совместим. Насколько мне известно, до Java 8 как скомпилированные классы, так и исходный код были совместимы с более поздними версиями JDK/JVM. Однако с добавлением методов по умолчанию в Java 8 это, похоже, больше не так.

Например, я использовал библиотеку, которая содержит реализацию java.util.List, включающую метод List<V> sort(). Этот метод возвращает копию содержимого списка, отсортированную. Библиотека, развернутая как jar-файл, хорошо работала в проекте, созданном с использованием JDK 1.8.

Однако позже мне пришлось перекомпилировать саму библиотеку с использованием JDK 1.8, и я обнаружил, что библиотека теперь не компилируется: класс, реализующий List, с его собственным методом sort() теперь конфликтует с дефолтным методом java.util.List.sort() в Java 8. Дефолтный метод sort() в Java 8 сортирует список на месте (возвращает void), в то время как метод sort() в моей библиотеке возвращает новый отсортированный список, что делает сигнатуры несовместимыми.

Основной вопрос:

  • Не приводит ли JDK 1.8 к потере совместимости исходного кода Java из-за методов по умолчанию?

Дополнительные вопросы:

  • Является ли это первым случаем такой утраты совместимости?
  • Обсуждалось ли это при проектировании и реализации методов по умолчанию? Зафиксировано ли это в документации?
  • Были ли неудобства (небольшие, надо признать) проигнорированы по сравнению с преимуществами?

Пример кода, который компилируется и работает под 1.7 и под 1.8, но не компилируется под 1.8:

import java.util.*;

public final class Sort8 {

    public static void main(String[] args) {
        SortableList<String> l = new SortableList<>(Arrays.asList(args));
        System.out.println("unsorted: "+l);
        SortableList<String> s = l.sort(Collections.reverseOrder());
        System.out.println("sorted  : "+s);
    }

    public static class SortableList<V> extends ArrayList<V> {

        public SortableList() { super(); }
        public SortableList(Collection<? extends V> col) { super(col); }

        public SortableList<V> sort(Comparator<? super V> cmp) {
            SortableList<V> l = new SortableList<>();
            l.addAll(this);
            Collections.sort(l, cmp);
            return l;
        }

    }

}

Вот как этот код компилируется (или не компилируется) и выполняется:

> c:\tools\jdk1.7.0_10\bin\javac Sort8.java

> c:\tools\jdk1.7.0_10\bin\java Sort8 this is a test
unsorted: [this, is, a, test]
sorted  : [this, test, is, a]

> del Sort8*.class

> c:\tools\jdk1.8.0_05\bin\javac Sort8.java
Sort8.java:46: error: sort(Comparator<? super V>) in SortableList cannot implement sort(Comparator<? super E>) in List
                public SortableList<V> sort(Comparator<? super V> cmp) {
                                       ^
  return type SortableList<V> is not compatible with void
  where V,E are type-variables:
    V extends Object declared in class SortableList
    E extends Object declared in interface List
1 error

Надеюсь, кто-то сможет помочь решить эту проблему.

3 ответ(ов)

0

Можно провести параллель с абстрактным классом. Абстрактный класс предназначен для наследования, чтобы абстрактные методы могли быть реализованы. Сам абстрактный класс содержит конкретные методы, которые вызывают абстрактные методы. Абстрактный класс может развиваться, добавляя новые конкретные методы, и это может привести к поломке подклассов.

Таким образом, точно такая же проблема, о которой вы упоминаете, существовала еще до Java 8. Эта проблема более ярко проявляется в API коллекций, поскольку существует множество подклассов, которые уже используют эти API.

Основной мотивацией для введения метода по умолчанию было добавление полезных методов в существующие API коллекций без нарушения работоспособности подклассов. При этом разработчикам пришлось проявлять значительное самообладание, чтобы не злоупотреблять этой возможностью из-за опасений сломать подклассы. Метод по умолчанию добавляется только в случае абсолютной необходимости. Вопрос в том, почему метод List.sort считается абсолютно необходимым. Это, я думаю, можно обсуждать.

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

0

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

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

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

0

Читая эту проблему, я задумался о её решении.

Методы по умолчанию помогли решить проблемы обратной совместимости, но проблемы с прямой совместимостью всё еще будут существовать.

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь