Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск


ГЛАВА 13

Совместимость на уровне двоичных кодов


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

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

Бумага, заключенная в кавычки выше, кажется в Продолжениях OOPSLA '95, опубликованной как ACM SIGPLAN Уведомления, Объем 30, Номер 10, октябрь 1995, страницы 426-438. В пределах платформы той бумаги двоичные файлы языка программирования Java двоичные совместимый при всех соответствующих преобразованиях, которые авторы идентифицируют (с некоторыми протестами относительно добавления переменных экземпляра). Используя их схему, вот список некоторых важных двоичных совместимых изменений, которые поддерживает язык программирования Java:

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

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

Эта глава сначала определяет некоторые свойства, которые любой двоичный формат для языка программирования Java должен иметь (§13.1). Это затем определяет совместимость на уровне двоичных кодов, объясняя, что это и что это не (§13.2). Это наконец перечисляет большой набор возможных изменений к пакетам (§13.3), классы (§13.4) и интерфейсы (§13.5), определяя, какие из этих изменений, как гарантируют, сохранят совместимость на уровне двоичных кодов и которые не являются.

13.1 Форма Двоичного файла

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

Необходимые свойства:

Двоичное представление для класса или интерфейса должно также содержать все следующее:

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

13.2 Что Совместимость на уровне двоичных кодов и Не

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

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

Язык программирования Java разрабатывается, чтобы предотвратить дополнения к контрактам и случайным коллизиям имени от повреждающейся совместимости на уровне двоичных кодов; определенно:

Совместимость на уровне двоичных кодов не является тем же самым как исходной совместимостью. В частности пример в §13.4.5 показывает, что ряд совместимых двоичных файлов может быть произведен из источников, которые не будут компилировать все вместе. Этот пример типичен: новое объявление добавляется, изменяя значение имени в неизменной части исходного кода, в то время как существующий ранее двоичный файл для той неизменной части исходного кода сохраняет полностью определенное, предыдущее значение имени. Создание непротиворечивого множества исходного кода требует обеспечения полностью определенного имени или выражения доступа к полю, соответствующего предыдущему значению.

13.3 Развитие Пакетов

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

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

13.4 Развитие Классов

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

13.4.1 abstract Классы

Если класс, который не был abstract изменяется, чтобы быть объявленным abstract, тогда существующие ранее двоичные файлы, которые пытаются создать новые экземпляры того класса, бросят любого InstantiationError во время ссылки, или (если отражающий метод используется), InstantiationException во время выполнения; такое изменение поэтому не рекомендуется для широко распределенных классов.

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

13.4.2 final Классы

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

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

13.4.3 public Классы

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

Если класс, который был объявлен public изменяется, чтобы не быть объявленным public, тогда IllegalAccessError бросается, если существующий ранее двоичный файл соединяется, что у потребностей, но больше есть доступ к типу класса; такое изменение не рекомендуется для широко распределенных классов.

13.4.4 Суперклассы и Суперинтерфейсы

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

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

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

Например, предположите что следующая тестовая программа:

class Hyper { char h = 'h'; } 
class Super extends Hyper { char s = 's'; }
class Test extends Super {
    public static void main(String[] args) {
        Hyper h = new Super();
        System.out.println(h.h);
    }
}
компилируется и выполняется, производя вывод:

h
Предположите что новая версия класса Super тогда компилируется:

class Super { char s = 's'; }
Эта версия класса Super не подкласс Hyper. Если мы тогда выполняем существующие двоичные файлы Hyper и Test с новой версией Super, тогда a VerifyError бросается во время ссылки. Верификатор возражает потому что результат new Super() не может быть присвоен переменной типа Hyper, потому что Super не подкласс Hyper.

Это поучительно, чтобы рассмотреть то, что могло бы произойти без шага проверки: программа могла бы работать и печать:

s
Это демонстрирует, что без верификатора система типов могла быть побеждена, соединяя непоследовательные двоичные файлы, даже при том, что каждый был произведен корректным компилятором Java.

Урок - то, что реализация, которая испытывает недостаток в верификаторе или сбоях, чтобы использовать это, не будет поддерживать безопасность типов и, поэтому, не допустимая реализация.

13.4.5 Тело класса и Задействованные Объявления

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

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

Если программа:

class Hyper {
	void hello() { System.out.println("hello from Hyper"); }
}
class Super extends Hyper {
	void hello() { System.out.println("hello from Super"); }
}
class Test {
	public static void main(String[] args) {
		new Super().hello();
	}
}
компилируется и выполняется, это производит вывод:

hello from Super
Предположите что новая версия класса Super производится:

class Super extends Hyper { }
тогда перекомпиляция Super и выполнение этого нового двоичного файла с исходными двоичными файлами для Test и Hyper производит вывод:

hello from Hyper
как ожидалось.

super ключевое слово может использоваться, чтобы получить доступ к методу, объявленному в суперклассе, обходя любые методы, объявленные в текущем классе. Выражение:

super.Identifier
разрешается, во время компиляции, к методу M в суперклассе S. Если метод M является методом экземпляра, то MR метода, вызванный во время выполнения, является методом с той же самой подписью как М., который является элементом прямого суперкласса класса, содержащего включение выражения super. Таким образом, если программа:

class Hyper {
	void hello() { System.out.println("hello from Hyper"); }
}
class Super extends Hyper { }
class Test extends Super {
	public static void main(String[] args) {
		new Test().hello();
	}
	void hello() {
		super.hello();
	}
}
компилируется и выполняется, это производит вывод:

hello from Hyper
Предположите что новая версия класса Super производится:

class Super extends Hyper {
	void hello() { System.out.println("hello from Super"); }
}
Если Super и Hyper перекомпилированы, но нет Test, тогда выполняя новые двоичные файлы с существующим двоичным файлом Test производит вывод:

hello from Super
поскольку Вы могли бы ожидать. (Дефект в некоторых ранних реализациях, вызванных их, чтобы напечатать:

hello from Hyper
неправильно.)

13.4.6 Доступ к Элементам и Конструкторам

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

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

Так, например, если пакет points определяет класс Point:

package points;
public class Point {
	public int x, y;
	protected void print() {
		System.out.println("(" + x + "," + y + ")");
	}
}
используемый Test программа:

class Test extends points.Point {
	protected void print() { System.out.println("Test"); }
	public static void main(String[] args) {
		Test t = new Test();
		t.print();
	}
}
тогда эти классы компиляция и Test выполняется, чтобы произвести вывод:

Test
Если метод print в классе Point изменяется, чтобы быть public, и затем только Point класс перекомпилирован, и затем выполнен с ранее существующим двоичным файлом для Test тогда никакая ошибка редактирования не происходит, даже при том, что это является неподходящим, во время компиляции, для a public метод, который будет переопределен a protected метод (как показано фактом, что класс Test не мог быть перекомпилирован, используя это новое Point класс, если печать не были изменены, чтобы быть public.)

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

13.4.7 Полевые Объявления

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

Примите ссылку на поле f с квалификацией типа T. Предположите далее, что f является фактически экземпляром (соответственно static) поле, объявленное в суперклассе T, S, и что тип f X. Если новое поле типа X с тем же самым именем как f добавляется к подклассу S, который является суперклассом T или T непосредственно, то ошибка редактирования может произойти. Такая ошибка редактирования произойдет, только если, в дополнение к вышеупомянутому, любому из следующих условий содержите:

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

Таким образом компилируя и выполняя код:

class Hyper { String h = "hyper"; }
class Super extends Hyper { String s = "super"; }
class Test {
	public static void main(String[] args) {
		System.out.println(new Super().h);
	}
}
производит вывод:

hyper
Изменение Super быть определенным как:

class Super extends Hyper {
	String s = "super";
	int h = 0;
}
перекомпиляция Hyper и Super, и выполнение получающихся новых двоичных файлов со старым двоичным файлом Test производит вывод:

hyper
Поле h из Hyper выводится исходным двоичным файлом main. В то время как это может казаться удивительным сначала, это служит, чтобы сократить количество несовместимостей, которые происходят во время выполнения. (В идеальном мире будут перекомпилированы все исходные файлы, которые нуждались в перекомпиляции всякий раз, когда любой из них изменился, устраняя такие неожиданности. Но такая массовая перекомпиляция часто непрактична или невозможна, особенно в Интернете. И, как был ранее отмечен, такая перекомпиляция будет иногда требовать дальнейших изменений к исходному коду.)

Как пример, если программа:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { }
class Test extends Super {
	public static void main(String[] args) {
		String s = new Test().h;
		System.out.println(s);
	}
}
компилируется и выполняется, это производит вывод:

Hyper
Предположите что новая версия класса Super тогда компилируется:

class Super extends Hyper { char h = 'h'; }
Если получающийся двоичный файл используется с существующими двоичными файлами для Hyper и Test, тогда вывод все еще:

Hyper
даже при том, что компиляция источника для этих двоичных файлов:

class Hyper { String h = "Hyper"; }
class Super extends Hyper { char h = 'h'; }
class Test extends Super {
	public static void main(String[] args) {
		String s = new Test().h;
		System.out.println(s);
	}
}
привел бы к ошибке времени компиляции, потому что h в исходном коде для main был бы теперь рассмотрен как обращающийся к char поле, объявленное в Super, и a char значение не может быть присвоено a String.

Удаление поля от класса повредит совместимость с любыми существующими ранее двоичными файлами, которые ссылаются на это поле, и a NoSuchFieldError будет брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Только private поля могут быть безопасно удалены из широко распределенного класса.

13.4.8 final Поля и Константы

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

Например, если программа:

class Super { static char s; }
class Test extends Super {
	public static void main(String[] args) {
		s = 'a';
		System.out.println(s);
	}
}
компилируется и выполняется, это производит вывод:

a
Предположите что новая версия класса Super производится:

class Super { final static char s = 'b'; }
Если Super перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом Test результаты в a IllegalAccessError.

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

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

Если пример:

class Flags { final static boolean debug = true; }
class Test {
	public static void main(String[] args) {
		if (Flags.debug)
			System.out.println("debug is true");
	}
}
компилируется и выполняется, это производит вывод:

debug is true
Предположите что новая версия класса Flags производится:

class Flags { final static boolean debug = false; }
Если Flags перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом Test производит вывод:

debug is true
потому что значение debug был постоянное время компиляции, и, возможно, использовалось в компиляции Test не делая ссылку на класс Flags.

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

Это поведение не изменилось бы если Flags были изменены, чтобы быть интерфейсом, как в измененном примере:

interface Flags { boolean debug = true; }
class Test {
	public static void main(String[] args) {
		if (Flags.debug)
			System.out.println("debug is true");
	}
}
(Одна причина требования встраивания констант является этим switch операторы требуют констант на каждом case, и никакие две таких постоянных величины не могут быть тем же самым. Компилятор проверяет на двойные постоянные величины в a switch оператор во время компиляции; class формат файла не делает символьного редактирования case значения.)

Лучший способ избежать проблем с "неустойчивыми константами" в широко распределенном коде состоит в том, чтобы объявить, поскольку константы времени компиляции только оценивают, которые действительно маловероятны когда-либо измениться. Много констант времени компиляции в интерфейсах являются маленькими целочисленными значениями, заменяющими перечислимые типы, которые не поддерживает язык; эти маленькие значения могут быть выбраны произвольно, и не должны должны быть быть изменены. Кроме для истинных математических констант, мы рекомендуем, чтобы исходный код сделал очень экономящее использование переменных класса, которые объявляются static и final. Если природа только для чтения final требуется, лучший выбор состоит в том, чтобы объявить a private static переменная и подходящий метод средства доступа, чтобы получить его значение. Таким образом мы рекомендуем:

private static int N;
public static int getN() { return N; }
вместо:

public static final int N = ...;
Нет никакой проблемы с:

public static int N = ...;
если N не должно быть только для чтения. Мы также рекомендуем, как правило, чтобы только действительно постоянные величины были объявлены в интерфейсах. Мы отмечаем, но не рекомендуем, что, если поле типа примитива интерфейса может измениться, его значение может быть выражено идиоматично как в:

interface Flags {
	boolean debug = new Boolean(true).booleanValue();
}
обеспечение, чтобы это значение не было константой. Подобные идиомы существуют для других типов примитивов.

Одна другая вещь отметить является этим static final поля, у которых есть постоянные величины (ли из примитивных или String введите), никогда должно казаться, не имеет начальное значение по умолчанию для их типа (§4.5.5). Это означает, что все такие поля, кажется, инициализируются сначала во время инициализации класса (§8.3.2.1, §9.3.1, §12.4.2).

13.4.9 static Поля

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

13.4.10 transient Поля

Добавление или удаление a transient модификатор поля не повреждает совместимость с существующими ранее двоичными файлами.

13.4.11 Метод и Объявления Конструктора

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

Примите ссылку на метод м. с квалификацией типа T. Предположите далее, что м. является фактически экземпляром (соответственно static) метод, объявленный в суперклассе T, S. Если новый метод типа X с той же самой подписью и типа возврата как м. добавляется к подклассу S, который является суперклассом T или T непосредственно, то ошибка редактирования может произойти. Такая ошибка редактирования произойдет, только если, в дополнение к вышеупомянутому, любому из следующих условий содержите:

Удаление метода или конструктора от класса может повредить совместимость с любым существующим ранее двоичным файлом, который ссылался на этот метод или конструктора; a NoSuchMethodError может быть брошен, когда такая ссылка от существующего ранее двоичного файла соединяется. Такая ошибка произойдет, только если никакой метод с соответствующей подписью и не возвращается, тип объявляется в суперклассе.

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

13.4.12 Метод и Параметры Конструктора

Изменять название формального параметра метода или конструктора не воздействует на существующие ранее двоичные файлы. Изменяя название метода, тип формального параметра к методу или конструктору, или добавление параметра к или удаление параметра от метода или объявления конструктора создают метод или конструктора с новой подписью, и имеют совместное воздействие удаления метода или конструктора со старой подписью и добавлением метода или конструктора с новой подписью (см. §13.4.11).

13.4.13 Тип Результата метода

Изменение типа результата метода, замена типа результата с void, или замена void с результатом у типа есть совместное воздействие удаления старого метода и добавления нового метода с новым типом результата или недавно void результат (см. §13.4.11).

13.4.14 abstract Методы

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

Изменение метода, который не объявляется abstract быть объявленным abstract повредит совместимость с существующими ранее двоичными файлами, которые ранее вызвали метод, вызывая AbstractMethodError.

Если пример программы:

class Super { void out() { System.out.println("Out"); } }
class Test extends Super {
	public static void main(String[] args) {
		Test t = new Test();
		System.out.println("Way ");
		t.out();
	}
}
компилируется и выполняется, это производит вывод:

Way
Out
Предположите что новая версия класса Super производится:

abstract class Super {
	abstract void out();
}
Если Super перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом Test результаты в a AbstractMethodError, потому что класс Test не имеет никакой реализации метода out, и, поэтому (или должен быть), краткий обзор.

13.4.15 final Методы

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

Если тестовая программа:

class Super { void out() { System.out.println("out"); } }
class Test extends Super {
	public static void main(String[] args) {
		Test t = new Test();
		t.out();
	}
	void out() { super.out(); }
}
компилируется и выполняется, это производит вывод:

out
Предположите что новая версия класса Super производится:

class Super { final void out() { System.out.println("!"); } }
Если Super перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом Test результаты в a VerifyError потому что класс Test ненадлежащим образом попытки переопределить метод экземпляра out.

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

Удаление final модификатор от метода не повреждает совместимость с существующими ранее двоичными файлами.

13.4.16 native Методы

Добавление или удаление a native модификатор метода не повреждает совместимость с существующими ранее двоичными файлами.

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

13.4.17 static Методы

Если метод, который не объявляется private был объявлен static (то есть, метод класса), и изменяется, чтобы не быть объявленным static (то есть, к методу экземпляра), или наоборот, тогда совместимость с существующими ранее двоичными файлами может быть повреждена, приводя к ошибке времени редактирования, а именно, IncompatibleClassChangeError, если эти методы используются существующими ранее двоичными файлами. Такие изменения не рекомендуются в коде, который был широко распределен.

13.4.18 synchronized Методы

Добавление или удаление a synchronized модификатор метода не повреждает совместимость с существующими двоичными файлами.

13.4.19 Метод и Броски Конструктора

Изменения к throws пункт методов или конструкторов не повреждает совместимость с существующими двоичными файлами; эти пункты проверяются только во время компиляции.

13.4.20 Метод и Тело Конструктора

Изменения к телу метода или конструктора не повреждают совместимость с существующими ранее двоичными файлами.

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

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

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

13.4.21 Метод и Конструктор, Перегружающийся

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

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

Если пример программы:

class Super {
	static void out(float f) { System.out.println("float"); }
}
class Test {
	public static void main(String[] args) {
		Super.out(2);
	}
}
компилируется и выполняется, это производит вывод:

float
Предположите что новая версия класса Super производится:

class Super {
	static void out(float f) { System.out.println("float"); }
	static void out(int i) { System.out.println("int"); }
}
Если Super перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом Test все еще производит вывод:

float
Однако, если Test тогда перекомпилирован, используя это новое Super, вывод тогда:

int
как, возможно, наивно ожидался в предыдущем случае.

13.4.22 Переопределение метода

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

13.4.23 Статические Инициализаторы

Добавление, удаляя, или изменяя статический инициализатор (§8.7) класса не воздействует на существующие ранее двоичные файлы.

13.5 Развитие Интерфейсов

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

13.5.1 public Интерфейсы

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

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

13.5.2 Суперинтерфейсы

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

13.5.3 Интерфейсные Элементы

Добавление метода к интерфейсу не повреждает совместимость с существующими ранее двоичными файлами. Поле, добавленное к суперинтерфейсу C, может скрыть поле, наследованное от суперкласса C. Если исходная ссылка была к полю экземпляра, IncompatibleClassChangeError закончится. Если исходная ссылка была присвоением, IllegalAccessError закончится.

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

Если пример программы:

interface I { void hello(); }
class Test implements I {
	public static void main(String[] args) {
		I anI = new Test();
		anI.hello();
	}
	public void hello() { System.out.println("hello"); }
}
компилируется и выполняется, это производит вывод:

hello
Предположите что новая версия интерфейса I компилируется:

interface I { }
Если I перекомпилирован, но нет Test, тогда выполняя новый двоичный файл с существующим двоичным файлом для Test приведет к a NoSuchMethodError. (В некоторых ранних реализациях все еще выполнилась эта программа; факт, что метод hello больше не существует в интерфейсе I не был правильно обнаружен.)

13.5.4 Полевые Объявления

Соображения для того, чтобы изменить полевые объявления в интерфейсах являются тем же самым как теми для static final поля в классах, как описано в §13.4.7 и §13.4.8.

13.5.5 Абстрактные Объявления метода

Соображения для того, чтобы изменить абстрактные объявления метода в интерфейсах являются тем же самым как теми для abstract методы в классах, как описано в §13.4.12, §13.4.13, §13.4.19, и §13.4.21.


Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск
Авторское право © Sun Microsystems, Inc 2000 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к jls@java.sun.com



Spec-Zone.ru - all specs in one place