2017-02-11 18 views
7

я следующий класс:Является ли тип возвращаемого типа общим с одинаковым двоичным совместимым стиранием?

public abstract Foo { 
    Foo() {} 

    public abstract Foo doSomething(); 

    public static Foo create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

Я хочу, чтобы изменить его к следующему определению:

public abstract Foo<T extends Foo<T>> { 
    Foo() {} 

    public abstract T doSomething(); 

    public static Foo<?> create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

Является ли это изменение двоичным совместимы? I.e., будет ли код, который скомпилирован против старой версии работы класса с новой версией без повторной публикации?

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

Из моего понимания, это должно быть в порядке, потому что стирание T является Foo, и, таким образом, подпись doSomething в байт-код такой же, как и раньше. Если я посмотрю на внутренние тиковые подписи, напечатанные javap -s, я действительно вижу, что это подтверждено (хотя подписи типа «не внутренние», напечатанные без -s, отличаются). Я тоже проверил это, и это сработало для меня.

Однако, Java API Compliance Checker сообщает мне, что две версии не совместимы с бинарными.

Итак, что именно? Является ли JLS гарантией двоичной совместимости здесь, или мне просто повезло в моих тестах? (Почему это могло случиться?)

ответ

4

Ну да, ваш код, похоже, не нарушает совместимость с двоичными файлами.
Я нашел это после некоторого ползания/чтением
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 , который говорит: -

Добавление или удаление параметра типа класса не сам по себе, имеет значение для бинарной совместимости.
...
Изменения первого связанного параметра типа класса может изменить стирание (§4.6) любой член, который использует этот параметр типа в своем собственном типе, и это может повлиять на бинарную совместимость. Изменение такой границы аналогично изменению первой границы параметра типа метода или конструктора (§ 13.4.13).

И это http://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_ones далее уточняет: -

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

У вас нет проблем с этого момента, так как вы впервые генерируете этот класс.

Но, пожалуйста, имейте в виду, как выше документ также говорит: -

Но также иметь в виду, что существуют серьезные ограничения, как тип или метод, который уже является общим может быть совместимо эволюционировали по отношению к его параметрам типа (см. таблицы выше). Поэтому, если вы планируете генерировать API, помните, что у вас есть только один шанс (релиз), чтобы все было правильно. В частности, если вы измените тип в сигнатуре API из необработанного типа «Список» на «Список <? >» или «Список < Объект >», вы будете заблокированы в этом решении. Мораль заключается в том, что генерация существующего API - это то, что следует рассматривать с точки зрения API в целом, а не по частям по методу или по принципу «один за другим».

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