Czy domyślne metody Javy 8 łamią kompatybilność źródeł?

Ogólnie rzecz biorąc, kod źródłowy Javy był zgodny z forward. Do Javy 8, o ile wiem, obie skompilowane klasy i source były kompatybilne z późniejszymi wydaniami JDK / JVM. [Aktualizacja: to nie jest poprawne, patrz komentarze re "enum", itp, poniżej.] Jednak po dodaniu domyślnych metod w Javie 8 wydaje się, że już tak nie jest.

Na przykład, Biblioteka, której używam, ma implementację java.util.List, która zawiera List<V> sort(). Metoda ta zwraca kopię zawartości posortowanej listy. Ta biblioteka, wdrożona jako zależność od pliku jar, działała dobrze w projekcie budowanym przy użyciu JDK 1.8.

Jednak później miałem okazję przekompilować samą bibliotekę używając JDK 1.8 i Okazało się, że biblioteka już nie kompiluje: klasa List-implementująca z własną metodą sort() jest teraz w konflikcie z domyślną metodą Java 8 java.util.List.sort(). Domyślna metoda Java 8 {[5] } sortuje listę w miejscu (zwraca void); Moja biblioteka sort() metoda-ponieważ zwraca nową posortowaną listę-ma niezgodny podpis.

Więc moje podstawowe pytanie brzmi:

  • czy JDK 1.8 nie wprowadza niekompatybilności kodu źródłowego Javy ze względu na domyślne metody?

Także:

  • czy to pierwsza taka niezgodna zmiana?
  • czy było to rozważane lub omawiane, gdy domyślne metody były projektowane i wdrażane? Czy jest to gdzieś udokumentowane?
  • był ( małe) niedogodności a korzyści?

Poniżej znajduje się przykład kodu, który kompiluje i działa pod 1.7 i działa pod 1.8-ale nie kompiluje się pod 1.8:

import java.util.*;

public final class Sort8 {

    public static void main(String[] args) {
        SortableList<String> l = new SortableList<String>(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<V>();
            l.addAll(this);
            Collections.sort(l, cmp);
            return l;
        }

    }

}

Poniższy kod pokazuje, że kod jest kompilowany (lub nie) i jest uruchamiany.

> 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]

> c:\tools\jdk1.8.0_05\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
Author: Paul, 2015-07-02

5 answers

czy JDK 1.8 nie wprowadza niekompatybilności kodu źródłowego Javy ze względu na domyślne metody?

Każda nowa metoda w superklasie lub interfejsie może złamać kompatybilność. Domyślne metody sprawiają, że jest mniej prawdopodobne, że zmiana interfejsu złamie kompatybilność. W tym sensie, że metody domyślne otwierają drzwi do dodawania metod do interfejsów, można powiedzieć, że metody domyślne mogą przyczynić się do pewnej zepsutej kompatybilności.

Czy to pierwsza taka niezgodna zmiana?

Prawie na pewno nie, ponieważ od wersji Java 1.0 podklasowujemy klasy z biblioteki standardowej.

czy było to rozważane lub omawiane podczas projektowania i wdrażania domyślnych metod? Czy jest to gdzieś udokumentowane?

Tak, to było rozważane. Zobacz artykuł Briana Goetza z sierpnia 2010 r. "Ewolucja interfejsu za pomocą metod "public defender" ":

  1. źródło zgodność [31]}

Jest możliwe, że ten schemat mógłby wprowadzić niezgodności źródłowe do tego stopnia, że interfejsy biblioteczne są modyfikowane w celu wstawiania nowych metod, które są niezgodne z metodami w istniejących klasach. (Na przykład, jeśli klasa ma metodę xyz() o wartości zmiennoprzecinkowej i implementuje kolekcję, a do kolekcji dodamy metodę xyz() o wartości int, istniejąca klasa nie będzie już kompilowana.)

czy (co prawda mała) niedogodność została zdyskontowana a korzyści?

Wcześniej zmiana interfejsu spowodowałaby definitywnie zerwanie kompatybilności. Teraz, to Może . Przechodzenie od "zdecydowanie" do "może" może być postrzegane pozytywnie lub negatywnie. Z jednej strony umożliwia dodawanie metod do interfejsów. Z drugiej strony, otwiera drzwi do tego rodzaju niezgodności, które widziałeś, nie tylko z klasami, ale także z interfejsami.

Korzyści są jednak większe niż niedogodności, jak przytoczono na szczyt pracy Goetza:

  1. stwierdzenie problemu

Po opublikowaniu nie można dodawać metod do interfejsu bez łamania istniejących implementacji. Im dłużej biblioteka została opublikowana, tym bardziej prawdopodobne jest, że ograniczenie to spowoduje smutek dla jej opiekunów.

Dodanie zamknięć do języka Java w JDK 7 kładzie dodatkowy nacisk na starzejące się interfejsy kolekcji; jeden z najważniejszych zaletą zamykania jest to, że umożliwia rozwój bardziej wydajnych bibliotek. Byłoby rozczarowaniem dodanie funkcji językowej, która umożliwia lepsze biblioteki, a jednocześnie nie rozszerza podstawowych bibliotek, aby mogły z niej korzystać.

 57
Author: Andy Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 15:38:22

Czy JDK 1.8 nie wprowadza niekompatybilności dla kodu źródłowego Javy ze względu na domyślne metody?

Tak, jak widziałeś siebie.

Czy to pierwsza taka niezgodna zmiana?

Nie. Słowo kluczowe Java 5 enumrównież się łamało, ponieważ wcześniej można było mieć zmienne nazwane tymi, które nie kompilowałyby się już w Javie 5 +

Czy było to rozważane lub omawiane, gdy domyślne metody były projektowane i wdrażane? Czy to udokumentowane?

Yes Orcale Java 8 Source incompatibility description

Czy (co prawda mała) niedogodność była zdyskontowana a korzyści?

TAK

 9
Author: dkatzel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 15:14:32

Możemy narysować równoległość z klasą abstrakcyjną. Klasa abstrakcyjna jest przeznaczona do podklasowania, tak aby metody abstrakcyjne mogły być zaimplementowane. Sama klasa abstrakcyjna zawiera konkretne metody, które wywołują metody abstrakcyjne. Klasa abstrakcyjna może swobodnie ewoluować poprzez dodanie bardziej konkretnych metod; i ta praktyka może rozbijać podklasy.

Dlatego dokładnie opisany problem istniał jeszcze przed Java8. Problem jest o wiele bardziej przejawiający się na API kolekcji, ponieważ istnieją wiele podklas na wolności.

Podczas gdy główną motywacją domyślnej metody było dodanie kilku przydatnych metod do istniejących API kolekcji bez łamania podklas, musieli oni ćwiczyć wielką samokontrolę robiąc to zbyt dużo, w obawie przed łamaniem podklas. Domyślna metoda jest dodawana tylko wtedy, gdy jest to absolutnie konieczne. Prawdziwe pytanie brzmi, dlaczego List.sort jest uważane za absolutnie konieczne. Myślę, że to dyskusyjne.

Niezależnie od tego, dlaczego wprowadzono domyślną metodę na pierwszym miejscu, jest to teraz świetne narzędzie dla projektantów API i powinniśmy traktować je tak samo jak konkretne metody w klasach abstrakcyjnych - muszą być starannie zaprojektowane z góry, a nowe muszą być wprowadzane z dużą ostrożnością.

 3
Author: ZhongYu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 17:10:20

Jak na ironię wprowadzono domyślne metody w interfejsach, aby pozwolić istniejącym bibliotekom używającym tych interfejsów , a nie na złamanie, wprowadzając jednocześnie ogromnąnową funkcjonalność w interfejsach. (kompatybilność wsteczna.)

Mogą pojawić się konflikty takie jak ta metoda sort. Coś do zapłaty za dodatkową funkcjonalność. W Twoim przypadku również coś do zbadania (należy zamiast tego użyć nowej funkcjonalności?).

Przerwy w kompatybilności Java forward są niewielkie, więcej w jego system pisania, który był stale powiększany. Najpierw z typami generycznymi, a teraz z typami wywnioskowanymi z interfejsów funkcjonalnych. Od wersji do wersji i od kompilatora do kompilatora występowały niewielkie różnice.

 2
Author: Joop Eggen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 15:32:16

Czytając ten problem, myślałem o jego rozwiązaniu.
domyślne metody rozwiązały problemy z kompatybilnością wsteczną, ale problemy z kompatybilnością wsteczną będą istniały.
myślę, że zamiast rozszerzać istniejące klasy, w takich przypadkach możemy mieć interfejsy specyficzne dla naszej aplikacji, aby dodać jakieś pożądane zachowanie do naszej klasy. Możemy zaimplementować ten interfejs aplikacji i używać go.

 0
Author: pfulara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-19 07:39:38