Spec-Zone .ru
спецификации, руководства, описания, API
Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Третий Выпуск


ГЛАВА 4

Типы, Значения, и Переменные


Язык программирования Java является языком со строгим контролем типов, что означает, что у каждой переменной и каждого выражения есть тип, который известен во время компиляции. Типы ограничивают значения, которые может содержать переменная (§4.12) или что выражение может произвести, ограничить операции, поддерживаемые на тех значениях, и определить значение операций. Строгий контроль типов помогает обнаружить ошибки во время компиляции.

Типы языка программирования Java делятся на две категории: типы примитивов и ссылочные типы. Типы примитивов (§4.2) boolean введите и числовые типы. Числовые типы являются целочисленными типами byte, short, int, long, и char, и типы с плавающей точкой float и double. Ссылочные типы (§4.3) являются типами классов, интерфейсными типами, и выстраивают типы. Есть также специальный нулевой тип. Объект (§4.3.1) является динамически создаваемым экземпляром типа класса или динамически создаваемым массивом. Значения ссылочного типа являются ссылками на объекты. Все объекты, включая массивы, поддерживают методы класса Object (§4.3.2). Строковые литералы представляются String объекты (§4.3.3).

Во время компиляции существуют типы. Некоторые типы соответствуют классам и интерфейсам, которые существуют во время выполнения. Корреспонденция между типами и классами или интерфейсами является неполной по двум причинам:

  1. Во время выполнения классы и интерфейсы загружаются виртуальной машиной Java, используя загрузчики класса. Каждый загрузчик класса определяет свой собственный набор классов и интерфейсов. В результате для двух загрузчиков возможно загрузить идентичный класс или интерфейсное определение, но произвести отличные классы или интерфейсы во время выполнения.
  2. Введите параметры и введите переменные (§4.4), не овеществляются во время выполнения. В результате различные параметризованные типы (§4.5) реализуются тем же самым классом или интерфейсом во время выполнения. Действительно, все вызовы данного универсального описания типа (§8.1.2, §9.1.2) совместно используют единственную реализацию времени выполнения.
Последствие (1) - то, которые кодируют, который скомпилировал, правильно может перестать работать во время ссылки, если загрузчики класса, которые загружают его, непоследовательны. См. бумагу Динамический Класс, Загружающийся в виртуальной машине Java, Шенгом Лиэнгом и Джилэдом Брэчей, в Продолжениях OOPSLA '98, опубликованный как ACM SIGPLAN Уведомления, Объем 33, Номер 10, октябрь 1998, страницы 36-44, и Спецификация виртуальной машины Java, Второй Выпуск для большего количества деталей.

Последствием (2) является возможность загрязнения "кучи" (§4.12.2.1). При определенных условиях возможно, что переменная параметризованного типа обращается к объекту, который не имеет того параметризованного типа. Переменная будет всегда обращаться к объекту, который является экземпляром класса, который реализует параметризованный тип. См. (§4.12.2) для дальнейшего обсуждения.

4.1 Виды Типов и Значений

Есть два вида, вводит язык программирования Java: типы примитивов (§4.2) и ссылочные типы (§4.3). Есть, соответственно, два вида значений данных, которые могут быть сохранены в переменных, передали как параметры, возвращенные методами, и работали на: примитивные значения (§4.2) и ссылочные значения (§4.3).


Type:
        PrimitiveType
        ReferenceType
        
Есть также специальный нулевой тип, тип выражения null, у которого нет никакого имени. Поскольку у нулевого типа нет никакого имени, невозможно объявить переменную нулевого типа или бросить к нулевому типу. Нулевая ссылка является единственным возможным значением выражения нулевого типа. Нулевая ссылка может всегда бросаться к любому ссылочному типу. Практически, программист может проигнорировать нулевой тип и только симулировать это null просто специальный литерал, который может иметь любой ссылочный тип.

4.2 Типы примитивов и Значения

Тип примитива предопределяется языком программирования Java и называется его зарезервированным словом (§3.9):


PrimitiveType:
        NumericType
        boolean

NumericType:
        IntegralType
        FloatingPointType

IntegralType: one of
        byte short int long char

FloatingPointType: one of
        float double
Примитивные значения не совместно используют состояние с другими примитивными значениями. Переменная, тип которой является типом примитива всегда, содержит примитивное значение того же самого типа. Значение переменной типа примитива может быть изменено только операциями присвоения на той переменной (включая инкремент (§15.14.2, §15.15.1) и декремент (§15.14.3, §15.15.2) операторы).

Числовые типы являются целочисленными типами и типами с плавающей точкой.

Целочисленные типы byte, short, int, и long, чьи значения являются 8-разрядными, 16-разрядными, 32-разрядными и 64-разрядными two's-дополнительными целыми числами со знаком, соответственно, и char, чьи значения являются 16-разрядными целыми без знака, представляющими элементы кода UTF-16 (§3.1).

Типы с плавающей точкой float, чьи значения включают 32-разрядный IEEE 754 числа с плавающей точкой, и double, чьи значения включают 64-разрядный IEEE 754 числа с плавающей точкой.

boolean у типа есть точно два значения: true и false.

4.2.1 Целочисленные типы и Значения

Значения целочисленных типов являются целыми числами в следующих диапазонах:

4.2.2 Целочисленные операции

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

Другие полезные конструкторы, методы, и константы предопределяются в классах Byte, Short, Integer, Long, и Character.

Если у целочисленного оператора кроме оператора сдвига есть по крайней мере один операнд типа long, тогда работа выполняется, используя 64-разрядную точность, и результат числового оператора имеет тип long. Если другой операнд не long, это сначала расширяется (§5.1.5), чтобы ввести long числовым продвижением (§5.6). Иначе, работа выполняется, используя 32-разрядную точность, и результат числового оператора имеет тип int. Если любой операнд не int, это сначала расширяется до типа int числовым продвижением.

Встроенные целочисленные операторы не указывают на переполнение или потерю значимости всегда. Целочисленные операторы могут бросить a NullPointerException если распаковывание преобразования (§5.1.8) нулевой ссылки требуется. Кроме этого, единственные целочисленные операторы, которые могут выдать исключение (§11), являются целочисленным оператором дележа / (§15.17.2) и целочисленный оператор остатка % (§15.17.3), которые бросают ArithmeticException если правый операнд является нулем, и инкрементными и декрементными операторами ++(§15.15.1, §15.15.2) и --(§15.14.3, §15.14.2), который может бросить OutOfMemoryError упаковывая преобразование (§5.1.7) требуется и нет достаточной памяти, доступной, чтобы выполнить преобразование.

Пример:

class Test {
        public static void main(String[] args) {
                int i = 1000000;
                System.out.println(i * i);
                long l = i;
                System.out.println(l * l);
                System.out.println(20296 / (l - i));
        }
}
производит вывод:

-727379968
1000000000000
и затем встречается ArithmeticException в подразделении l - i, потому что l - i нуль. Первое умножение выполняется в 32-разрядной точности, тогда как второе умножение является a long умножение. Значение -727379968 десятичное значение низких 32 битов математического результата, 1000000000000, который является значением, слишком большим для типа int.

Любое значение любого целочисленного типа может быть брошено к или от любого числового типа. Нет никаких бросков между целочисленными типами и типом boolean.

4.2.3 Типы с плавающей точкой, Форматы, и Значения

Типы с плавающей точкой float и double, которые концептуально связываются с 32-разрядной одинарной точностью и двойная точность 64-разрядные значения формата IEEE 754 и операции как определено в Стандарте IEEE для Двоичной Арифметики С плавающей точкой, Стандарт ANSI/IEEE 754-1985 (IEEE, Нью-Йорк).

Стандарт IEEE 754 включает не только положительные и отрицательные числа, которые состоят из знака и величины, но также и положительных и отрицательных нулей, положительные и отрицательные бесконечности, и специальные значения Не-числа (после этого сокращал НЭН). Значение НЭН используется, чтобы представить результат определенных недопустимых операций, таких как делящийся нуль нулем. Константы НЭН обоих float и double тип предопределяется как Float.NaN и Double.NaN.

Каждая реализация языка программирования Java обязана поддерживать два стандартных набора значений с плавающей точкой, названных набором значений плавающим и двойным набором значений. Кроме того, реализация языка программирования Java может поддерживать или или обе из двух расширенных экспонент наборы значений с плавающей точкой, названные набором значений "расширенная экспонента плавающая" и набор значений "двойная расширенная экспонента". Эти наборы значений расширенной экспоненты, при определенных обстоятельствах, могут использоваться вместо стандартных наборов значений, чтобы представить значения выражений типа float или double (§5.1.13, §15.4).

Конечные ненулевые значения любого набора значений с плавающей точкой могут все быть выражены в форме s · м. · 2 (e-N+1), где s +1 или-1, м. является положительным целым числом меньше чем 2N, и e является целым числом между Эмином = - (2K-1-2) и Emax = 2K-1-1, включительно, и где N и K являются параметрами, которые зависят от набора значений. Некоторые значения могут быть представлены в этой форме больше чем одним способом; например, если значение v в наборе значений могло бы быть представлено в этой форме, используя определенные значения для s, м., и e, затем если бы это произошло, которым м. был даже, и e были меньше чем 2K-1, то можно было разделить на два м. и увеличить e на 1, чтобы произвести второе представление для того же самого значения v. Представление в этой форме вызывают нормализованное если м. 2 (N-1); иначе представление, как говорят, денормализовывается. Если значение в наборе значений не может быть представлено таким способом, которым м. 2 (N-1), то значение, как говорят, является денормализованным значением, потому что у этого нет никакого нормализованного представления.

Ограничения на параметры N и K (и на полученные параметры Эмин и Эмэкс) для этих требуемых двух и два дополнительных набора значений с плавающей точкой получаются в итоге в Таблице 4.1.

Параметр плавание "пустите в ход расширенную экспоненту" двойной "удвойте расширенную экспоненту"
N

24

24

53

53

K

8

11

11

15

Emax

+127

+1023

+1023

+16383

Эмин

-126

-1022

-1022

-16382

Где один или оба набора значений расширенной экспоненты поддерживаются реализацией, затем для каждого поддерживаемого набора значений расширенной экспоненты есть определенный зависящий от реализации постоянный K, значение которого ограничивается Таблицей 4.1; это значение K поочередно диктует значения для Эмина и Эмэкса.

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

Отметьте, что ограничения в Таблице 4.1 разрабатываются так, чтобы каждый элемент набора значений плавающего был обязательно также элементом набора значений "расширенная экспонента плавающая", двойной набор значений, и набор значений "двойная расширенная экспонента". Аналогично, каждый элемент двойного набора значений является обязательно также элементом набора значений "двойная расширенная экспонента". Каждый набор значений расширенной экспоненты имеет больший диапазон значений экспоненты чем соответствующий стандартный набор значений, но не имеет большей точности.

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

Плавание, "расширенная экспонента плавающая", дважды, и наборы значений "двойная расширенная экспонента" не является типами. Это всегда корректно для реализации языка программирования Java, чтобы использовать элемент набора значений плавающего, чтобы представить значение типа float; однако, может быть допустимо в определенных областях кода для реализации использовать элемент набора значений "расширенная экспонента плавающая" вместо этого. Точно так же это всегда корректно для реализации, чтобы использовать элемент двойного набора значений, чтобы представить значение типа double; однако, может быть допустимо в определенных областях кода для реализации использовать элемент набора значений "двойная расширенная экспонента" вместо этого.

За исключением НЭН, упорядочиваются значения с плавающей точкой; расположенный от самого маленького до самого большого, они - отрицательная бесконечность, отрицательные конечные ненулевые значения, положительный и отрицательный нуль, положительные конечные ненулевые значения, и положительная бесконечность.

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

По большей части платформа Java обрабатывает значения НЭН данного типа, как если бы свернутый в единственное каноническое значение (и следовательно эта спецификация обычно обращается к произвольной НЭН как если бы к каноническому значению). Однако, версия 1.3 платформа Java представленные методы, позволяющие программисту различать значения НЭН: Float.floatToRawIntBits и Double.doubleToRawLongBits методы. Заинтересованный читатель относится в спецификации для Float и Double классы для получения дополнительной информации.

Положительный нулевой и отрицательный нуль сравнивается равный; таким образом результат выражения 0.0==-0.0 true и результат 0.0>-0.0 false. Но другие операции могут отличить положительный и отрицательный нуль; например, 1.0/0.0 имеет значение положительная бесконечность, в то время как значение 1.0/-0.0 отрицательная бесконечность.

НЭН неупорядочивают, таким образом, числовые операторы сравнения <, <=, >, и >= возвратиться false если или или оба операнда НЭН (§15.20.1). Оператор равенства == возвраты false если любым операндом является НЭН, и оператор неравенства != возвраты true если любым операндом является НЭН (§15.21.1). В частности x!=x true если и только если x НЭН, и (x<y) == !(x>=y) будет false если x или y НЭН.

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

4.2.4 Операции с плавающей точкой

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

Другие полезные конструкторы, методы, и константы предопределяются в классах Float, Double, и Math.

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

Если по крайней мере один из операндов числовому оператору имеет тип double, тогда работа выполняется, используя 64-разрядную арифметику с плавающей точкой, и результатом числового оператора является значение типа double. (Если другой операнд не является a double, это сначала расширяется до типа double числовым продвижением (§5.6).) Иначе, работа выполняется, используя 32-разрядную арифметику с плавающей точкой, и результатом числового оператора является значение типа float. Если другой операнд не является a float, это сначала расширяется до типа float числовым продвижением.

Операторы на числах с плавающей точкой ведут себя как определено IEEE 754 (за исключением оператора остатка (§15.17.3)). В частности язык программирования Java требует поддержки IEEE 754 денормализованные числа с плавающей точкой и постепенная потеря значимости, которые облегчают доказывать требуемые свойства определенных числовых алгоритмов. Операции с плавающей точкой "не сбрасывают, чтобы обнулить", если расчетным результатом является денормализованное число.

Язык программирования Java требует, чтобы арифметика с плавающей точкой вела себя как будто каждый оператор с плавающей точкой, округленный ее результат с плавающей точкой к точности результата. Неточные результаты должны быть округлены к представимому значению, самому близкому к бесконечно точному результату; если два самых близких представимых значения одинаково рядом, тот с его младшим значащим разрядным нулем выбирается. Это - значение по умолчанию стандарта IEEE 754 округление режима, известного как вокруг самому близкому.

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

Операторы с плавающей точкой могут бросить a NullPointerException если распаковывание преобразования (§5.1.8) нулевой ссылки требуется. Кроме этого, единственные операторы с плавающей точкой, которые могут выдать исключение (§11), являются инкрементными и декрементными операторами ++(§15.15.1, §15.15.2) и --(§15.14.3, §15.14.2), который может бросить OutOfMemoryError упаковывая преобразование (§5.1.7) требуется и нет достаточной памяти, доступной, чтобы выполнить преобразование.

Работа, которая переполнения производят бесконечность со знаком, работа, которая потери значимости производят денормализованное значение или нуль со знаком, и работа, у которой нет никакого математически определенного результата, производит НЭН. Все числовые операции с НЭН как операнд производят НЭН в результате. Как был уже описан, НЭН неупорядочивают, таким образом, числовая работа сравнения, включающая один или два возврата NaNs false и любой != сравнение, включающее возвраты НЭН true, включая x!=x когда x НЭН.

Пример программы:

class Test {
        public static void main(String[] args) {
                // An example of overflow:
                double d = 1e308;
                System.out.print("overflow produces infinity: ");
                System.out.println(d + "*10==" + d*10);
                // An example of gradual underflow:
                d = 1e-305 * Math.PI;
                System.out.print("gradual underflow: " + d + "\n      ");
                for (int i = 0; i < 4; i++)
                        System.out.print(" " + (d /= 100000));
                System.out.println();
                // An example of NaN:
                System.out.print("0.0/0.0 is Not-a-Number: ");
                d = 0.0/0.0;
                System.out.println(d);
                // An example of inexact results and rounding:
                System.out.print("inexact results with float:");
                for (int i = 0; i < 100; i++) {
                        float z = 1.0f / i;
                        if (z * i != 1.0f)
                                System.out.print(" " + i);
                }
                System.out.println();
                // Another example of inexact results and rounding:
                System.out.print("inexact results with double:");
                for (int i = 0; i < 100; i++) {
                        double z = 1.0 / i;
                        if (z * i != 1.0)
                                System.out.print(" " + i);
                }
                System.out.println();
                // An example of cast to integer rounding:
                System.out.print("cast to int rounds toward 0: ");
                d = 12345.6;
                System.out.println((int)d + " " + (int)(-d));
        }
}
производит вывод:

overflow produces infinity: 1.0e+308*10==Infinity
gradual underflow: 3.141592653589793E-305
        3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0
0.0/0.0 is Not-a-Number: NaN
inexact results with float: 0 41 47 55 61 82 83 94 97
inexact results with double: 0 49 98
cast to int rounds toward 0: 12345 -12345

Этот пример демонстрирует, между прочим, что постепенная потеря значимости может привести к постепенной потере точности.

Результаты, когда i 0 включите подразделение нулем, так, чтобы z становится положительной бесконечностью, и z * 0 НЭН, которая не равна 1.0.

4.2.5 Булев Тип и булевы значения

boolean тип представляет логическое количество с двумя возможными значениями, обозначенными литералами true и false (§3.10.3). Булевы операторы:

Булевы выражения определяют поток управления в нескольких видах операторов:

A boolean выражение также определяет, какое подвыражение оценивается в условном выражении ? : оператор (§15.25).

Только boolean или Boolean выражения могут использоваться в операторах управления и как первый операнд условного оператора ? :. Целое число x может быть преобразован в a boolean, после соглашения языка C, которое любое ненулевое значение true, по выражению x!=0. Ссылка на объект obj может быть преобразован в a boolean, после соглашения языка C, что любая ссылка кроме null true, по выражению obj!=null.

Бросок a boolean оцените типу boolean или Boolean позволяется (§5.1.1); никто другой не набирает тип boolean позволяются. A boolean может быть преобразован в строку преобразованием строк (§5.4).

4.3 Ссылочные типы и Значения

Есть три вида ссылочных типов: типы классов (§8), интерфейсные типы (§9), и типы массива (§10). Ссылочные типы могут быть параметризованы (§4.5) с параметрами типа (§4.4).


ReferenceType:
        ClassOrInterfaceType
        TypeVariable
        ArrayType

ClassOrInterfaceType:
        ClassType
        InterfaceType


ClassType:
        TypeDeclSpecifier TypeArgumentsopt

InterfaceType:
        TypeDeclSpecifier TypeArgumentsopt

TypeDeclSpecifier:
        TypeName
        ClassOrInterfaceType . Identifier
        


TypeName:
        Identifier
        TypeName . Identifier

TypeVariable:
        Identifier

ArrayType:
        Type [ ]
Класс или интерфейсный тип состоят из спецификатора описания типа, дополнительно сопровождаемого параметрами типа (когда это - параметризованный тип). Введите параметры, описываются в (§4.5.1).

Спецификатор описания типа может быть или именем типа (§6.5.5), или классом или соединить интерфейсом с типом, сопровождаемым "." и идентификатор. В последнем случае у спецификатора есть форма T.id, где идентификатор должен быть простым именем доступного (§6.6) типа элемента (§8.5, §9.5) T, или ошибка времени компиляции происходит. Спецификатор обозначает тот тип элемента.

Пример кода:

class Point { int[] metrics; }
interface Move { void move(int deltax, int deltay); }
объявляет тип класса Point, интерфейсный тип Move, и использует тип массива int[] (массив int) объявить поле metrics из класса Point.

4.3.1 Объекты

Объект является экземпляром класса или массивом.

Ссылочные значения (часто только ссылки) являются указателями на эти объекты, и специальной нулевой ссылкой, которая не обращается ни к какому объекту.

Экземпляр класса явно создается выражением создания экземпляра класса (§15.9). Массив явно создается выражением создания массива (§15.10).

Новый экземпляр класса неявно создается, когда оператор конкатенации строк + (§15.18.1) используется в непостоянном (§15.28) выражении, приводящем к новому объекту типа String (§4.3.3). Новый объект массива неявно создается, когда выражение инициализатора массива (§10.6) оценивается; это может произойти, когда класс или интерфейс инициализируются (§12.4), когда новый экземпляр класса создается (§15.9), или когда оператор объявления локальной переменной выполняется (§14.4). Новые объекты Булевской переменной типов, Байта, Короткого, Символ, Целое число, Долго, Плавание и Двойной, могут быть неявно созданы, упаковывая преобразование (§5.1.7).

Многие из этих случаев иллюстрируются в следующем примере:

class Point {
        int x, y;
        Point() { System.out.println("default"); }
        Point(int x, int y) { this.x = x; this.y = y; }
        // A Point instance is explicitly created at class initialization time:
        static Point origin = new Point(0,0);
        // A String can be implicitly created by a + operator:
        public String toString() {
                return "(" + x + "," + y + ")";
        }
}
class Test {
        public static void main(String[] args) {
                // A Point is explicitly created using newInstance:
                Point p = null;
                try {
                        p = (Point)Class.forName("Point").newInstance();
                } catch (Exception e) {
                        System.out.println(e);
                }
                // An array is implicitly created by an array constructor:
                Point a[] = { new Point(0,0), new Point(1,1) };
                // Strings are implicitly created by + operators:
                System.out.println("p: " + p);
                System.out.println("a: { " + a[0] + ", "
                                                                                   + a[1] + " }");
                // An array is explicitly created by an array creation expression:
                String sa[] = new String[2];
                sa[0] = "he"; sa[1] = "llo";
                System.out.println(sa[0] + sa[1]);
        }
}
который производит вывод:

default
p: (0,0)
a: { (0,0), (1,1) }
hello
Операторы на ссылках на объекты:

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

Пример программы:

class Value { int val; }
class Test {
        public static void main(String[] args) {
                int i1 = 3;
                int i2 = i1;
                i2 = 4;
                System.out.print("i1==" + i1);
                System.out.println(" but i2==" + i2);
                Value v1 = new Value();
                v1.val = 5;
                Value v2 = v1;
                v2.val = 6;
                System.out.print("v1.val==" + v1.val);
                System.out.println(" and v2.val==" + v2.val);
        }
}
производит вывод:

i1==3 but i2==4
v1.val==6 and v2.val==6
потому что v1.val и v2.val сошлитесь на ту же самую переменную экземпляра (§4.12.3) в том Value объект создается единственным new выражение, в то время как i1 и i2 различные переменные.

См. §10 и §15.10 для примеров создания и использования массивов.

У каждого объекта есть связанная блокировка (§17.1), который используется synchronized методы (§8.4.3) и synchronized оператор (§14.19), чтобы обеспечить управление параллельным доступом, чтобы утвердить многократными потоками (§17).

4.3.2 Объект Класса

Класс Object суперкласс (§8.1) всех других классов. Переменная типа Object может содержать ссылку на нулевую ссылку или на любой объект, является ли это экземпляром класса или массива (§10). Весь класс и типы массива наследовали методы класса Object, которые получаются в итоге здесь:

package java.lang;

public class Object {
    public final Class<?> getClass() { . . . }
    public String toString() { . . . }
    public boolean equals(Object obj) { . . . }
    public int hashCode() { . . . }
    protected Object clone()
            throws CloneNotSupportedException { . . . }
    public final void wait()
                throws IllegalMonitorStateException,
                        InterruptedException { . . . }
    public final void wait(long millis)
            throws IllegalMonitorStateException,
                    InterruptedException { . . . }
    public final void wait(long millis, int nanos) { . . . }
            throws IllegalMonitorStateException,
                    InterruptedException { . . . }
    public final void notify() { . . . }
            throws IllegalMonitorStateException
    public final void notifyAll() { . . . }
            throws IllegalMonitorStateException
    protected void finalize()
            throws Throwable { . . . }
}
Элементы Object следующие:

4.3.3 Строка Класса

Экземпляры класса String представьте последовательности символов Unicode. A String у объекта есть постоянное (неизменное) значение. Строковые литералы (§3.10.5) являются ссылками на экземпляры класса String.

Оператор конкатенации строк + (§15.18.1) неявно создает новое String возразите, когда результатом не является время компиляции, постоянное (§15.28).

4.3.4 Когда Ссылочные типы Являются Тем же самым

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

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

Два ссылочных типа являются тем же самым типом времени выполнения если:

4.4 Введите Переменные

Переменная типа (§4.4) является неполным идентификатором. Переменные типа представляются универсальными объявлениями класса (§8.1.2) универсальные интерфейсные объявления (§9.1.2) универсальные объявления метода (§8.4.4) и универсальными объявлениями конструктора (§8.8.4).

TypeParameter:
        TypeVariable TypeBoundopt

TypeBound:
        extends ClassOrInterfaceType AdditionalBoundListopt

AdditionalBoundList:
        AdditionalBound AdditionalBoundList
        AdditionalBound

AdditionalBound:
        & InterfaceType
Переменным типа связывали дополнительное, T & I1... В. Связанный состоит или из переменной типа, или из класс или интерфейсный тип T, возможно сопровождаемый дальнейшим интерфейсом, вводят I1..., В. Если не связано дается для переменной типа, Object принимается. Это - ошибка времени компиляции если любой из типов I1... В переменная типа или тип класса. Стирания (§4.6) всех составляющих типов связанного должны попарно отличаться, или ошибка времени компиляции происходит. Порядок вводит связанный, является только существенным в этом, стирание переменной типа определяется первым, вводят ее связанный, и что тип класса или вводит переменную, может только появиться в первой позиции.

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

См. раздел §6.3 для правил, определяющих контекст переменных типа.

Элементы переменной типа X со связанной T & I1... В элементы перекрестного типа (§4.9) T & I1... В появлении в точке, где переменная типа объявляется.


Обсуждение

Следующий пример иллюстрирует, какие элементы переменная типа имеет.

package TypeVarMembers;

        class C { 
                void mCDefault() {}     
                public void mCPublic() {}       
                private void mCPrivate() {} 
                protected void mCProtected() {} 
        } 
        class CT extends C implements I {}
        interface I {   
                void mI(); } 
                <T extends C & I> void test(T t) {    
                        t.mI(); // OK
                        t.mCDefault(); // OK
                        t.mCPublic(); // OK 
                        t.mCPrivate(); // compile-time error
                        t.mCProtected(); // OK 
                } 
        }
У переменной типа T есть те же самые элементы как перекрестный тип C & я, у которого поочередно есть те же самые элементы как пустой CT класса, определенный в том же самом контексте с помощью эквивалентных супертипов. Элементы интерфейса всегда общедоступны, и поэтому всегда наследованные (если не переопределено). Следовательно ми является элементом CT и T. Среди элементов C все кроме mCPrivate наследованы CT, и являются поэтому элементами и CT и T.

Если бы C был объявлен в различном пакете чем T, то звонок mCDefault дал бы начало ошибке времени компиляции, поскольку тот элемент не будет доступен в точке, где T объявляется.


4.5 Параметризованные Типы

Параметризованный тип состоит из класса или интерфейсного имени C и фактического списка параметров типа <T1..., Tn>. Это - ошибка времени компиляции, если C не является именем универсального класса или интерфейса, или если число параметров типа в фактическом списке параметров типа отличается от числа объявленных параметров типа C. В следующем, всякий раз, когда мы говорим о классе или интерфейсном типе, мы включаем универсальную версию также, если явно не исключено. Всюду по этому разделу позвольте A1..., быть формальными параметрами типа C, и позволять быть висмутом быть объявленным, связанным Ая. Нотация [Ай: = Ti] обозначает замену переменной типа Ай с типом Ti, для 1 дюйма, и используется всюду по этой спецификации.

Позвольте P = Г <T1..., Tn> быть параметризованным типом. Это должно иметь место, что, после того, как P подвергается, чтобы получить преобразование (§5.1.10) приводящий к Г типа <X1..., Xn>, для каждого фактического параметра типа Xi, 1 дюйм, Xi <: Висмут [A1: = X1...: = Xn] (§4.10), или ошибка времени компиляции происходит.


Обсуждение

Пример: Параметризованные типы.

Vector<String> 
Seq<Seq<A>> 
Seq<String>.Zipper<Integer>
Collection<Integer> 
Pair<String,String>

// Vector<int> -- illegal, primitive types cannot be arguments
// Pair<String> -- illegal, not enough arguments
// Pair<String,String,String> -- illegal, too many arguments


Два параметризованных типа доказуемо отличны, если любое из следующих условий содержит:

4.5.1 Введите Параметры и Подстановочные знаки

Параметрами типа могут быть или ссылочные типы или подстановочные знаки.


TypeArguments:
        < ActualTypeArgumentList >

ActualTypeArgumentList: 
        ActualTypeArgument
        ActualTypeArgumentList , ActualTypeArgument


ActualTypeArgument:
        ReferenceType
        Wildcard

Wildcard:
? WildcardBoundsOpt



WildcardBounds:
        extends ReferenceType
        super ReferenceType
        


Обсуждение

Примеры

void printCollection(Collection<?> c) {  // a wildcard collection
  for (Object o : c) {
    System.out.println(o);
  }
}

Отметьте, что, используя Набор <Объект>, поскольку тип входящего параметра, c, не был бы почти как полезный; метод мог только использоваться с фактическим параметром, у которого был Набор типа <Объект>, который будет довольно редок. Напротив, использование неограниченного подстановочного знака позволяет любому виду набора использоваться в качестве параметра.


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


Обсуждение

Пример - Подстановочный знак параметризованные типы как компонентные типы типов массива.

public Method getMethod(Class<?>[] parameterTypes) { ... }

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

? extends B
, где B является связанным.


Обсуждение

Пример: Ограниченные подстановочные знаки.

boolean addAll(Collection<? extends E> c)

Здесь, метод объявляется в пределах интерфейсного Набора <E>, и разрабатывается, чтобы добавить все элементы его входящего параметра набору, на который это вызывается. Естественное стремление должно было бы использовать Набор <E> в качестве типа c, но это является излишне рестриктивным. Альтернатива должна была бы объявить, что метод непосредственно был универсален:

<T> boolean addAll(Collection<T> c)

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


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

? super B
, где B является нижней границей.


Обсуждение

Пример: Нижние границы на подстановочных знаках.

Reference(T referent, ReferenceQueue<? super T> queue);
Здесь, референт может быть вставлен в любую очередь, тип элемента которой является типом высшего качества типа T референта.

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


Обсуждение

Отношение подстановочных знаков к установленной теории типов является интересным, на которое мы кратко ссылаемся здесь.

Подстановочные знаки являются ограниченной формой экзистенциальных типов. Учитывая универсальное описание типа Г <T расширяет B>, Г <?> походит на примерно приблизительно X <: B. Г <X>.

Читатели, заинтересованные более всесторонним обсуждением, должны обратиться к На Основанном на различии Выделении подтипов для Параметрических Типов Ацуши Игараши и Мирко Вироли в продолжениях 16-ой европейской Конференции по Объектно-ориентированному программированию (ECOOP 2002).

Подстановочные знаки отличаются по определенным деталям от конструкций, описанных в вышеупомянутой газете, в особенности в использовании преобразования получения (§5.1.10) ratther чем операция закрытия, описанная Igarashi и Viroli. Для формальной учетной записи подстановочных знаков см. Дикий FJ Мэдсом Торджерсеном, Эриком Эрнстом и Кристианом Плеснером Хансеном, на 12-ой мастерской на Основах Объектно-ориентированного программирования (ДУРАК 2005).

Исторически, подстановочные знаки являются прямым потомком работы Ацуши Игараши и Мирко Вироли. Эта сама работа полагается на более раннюю работу Крестеном Торупом и Мэдсом Торджерсеном ("Объединяющий Genericity", ECOOP 99), так же как давняя традиция работы над объявлением базируемое различие, которое возвращается к работе Пьера Америки над ПУЛОМ (OOPSLA 89)


4.5.1.1 Введите Включение Параметра и Эквивалентность

TA1 параметра типа, как говорят, содержит другой параметр типа TA2, записанный TA2 <= TA1, если набор типов, обозначенных TA2, является доказуемо подмножеством набора типов, обозначенных TA1 по следующим правилам (где <: обозначает выделение подтипов (§4.10)):

4.5.2 Элементы и Конструкторы Параметризованных Типов

Позвольте C быть классом или интерфейсным объявлением с формальными параметрами типа A1..., и позволять C <T1..., Tn> быть вызовом C, где для 1 дюйма Ti являются типами (а не подстановочные знаки). Затем:

Если каким-либо из параметров типа параметризованному типу являются подстановочные знаки, тип его элементов и конструкторов неопределен.


Обсуждение

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


4.6 Введите Стирание

Стирание типа является отображением от типов (возможно включая параметризованные типы, и введите переменные) к типам (который, никогда не параметризованы типы или вводят переменные). Мы пишем |T | для стирания типа T. Отображение стирания определяется следующим образом.

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

4.7 Типы Reifiable

Поскольку некоторая информация о типе стирается во время компиляции, не, все типы доступны во время выполнения. Типы, которые абсолютно доступны во время выполнения, известны как reifiable типы. Тип является reifiable, если и только если одно из следующего содержит:


Обсуждение

Решение не сделать все универсальные типы reifiable является одним из самых решающих, и спорных проектных решений, включающих систему типов языка.

В конечном счете самое важное побуждение для этого решения является совместимостью с существующим кодом.

Наивно, у добавления новых конструкций, таких как genericity нет никаких импликаций для существующего ранее кода. Язык программирования по существу, является совместимым с более ранними версиями, пока каждая программа, записанная в предыдущих версиях, сохраняет свое значение в новой версии. Однако, это понятие, которое можно назвать совместимостью языка, имеет просто теоретический интерес. Реальные программы (даже тривиальные, такой как "Привет Мир") составляются из нескольких единиц компиляции, некоторые из которых обеспечиваются платформой Java (такой как элементы java.lang или java.util).

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

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

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

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

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


4.8 Необработанные Типы

Чтобы облегчить взаимодействие через интерфейс с неуниверсальным кодом наследства, также возможно использовать в качестве типа стирание (§4.6) параметризованного типа (§4.5). Такой тип вызывают необработанным типом.

Более точно необработанный тип, определяют, чтобы быть также:


Обсуждение

Последняя точка, возможно, не сразу самоочевидна. Представляя для Вашего рассмотрения, тогда, следующего примера:

class Outer<T>{
        T t;
        class Inner {
                T setOuterT(T t1) {t = t1;return t;}
        }
}
Тип элемента (ов) Inner зависит от параметра типа Outer. Если Outer сырые данные, Inner должен быть обработан как сырые данные также, поскольку их не допустимая привязка для T.

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



Обсуждение

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

class Outer<T>{
        class Inner<S> {
                S s;
        }
}
это не возможно к доступу Inner как частично необработанный тип ("редкий" тип)
Outer.Inner<Double> x = null; // illegal
Double d = x.s;
потому что Outer непосредственно сырые данные, так все его внутренние классы, включая Inner, и таким образом, не возможно передать любые параметры типа к этому.


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

Это - ошибка времени компиляции, чтобы попытаться использовать элемент типа параметризованного типа как необработанный тип.


Обсуждение

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

Outer<Integer>.Inner x = null; // illegal
Это - противоположность случая, который мы обсуждали выше. Нет никакого практического выравнивания для этой половины испеченного типа. В коде наследства не используются никакие параметры типа. В коде ненаследства мы должны использовать универсальные типы правильно и передать все необходимые фактические параметры типа.



Обсуждение

Переменные необработанного типа могут быть присвоены от значений любого из параметрических экземпляров типа.

Например, возможно присвоить a Vector<String> к a Vector, основанный на правилах выделения подтипов (§4.10.2).

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


Суперклассы (соответственно, суперинтерфейсы) необработанного типа являются стираниями суперклассов (суперинтерфейсы) любого из его параметризованных вызовов.

Тип конструктора (§8.8), метод экземпляра (§8.8, §9.4), или нестатическое поле (§8.3) М. необработанного типа C, который не наследован от его суперклассов или суперинтерфейсов, является стиранием своего типа в универсальном объявлении, соответствующем C. Тип статического элемента необработанного типа C является тем же самым как своим типом в универсальном объявлении, соответствующем C.

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

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

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

Супертип класса может быть необработанным типом. Задействованные доступы для класса обрабатываются как нормальные, и задействованные доступы для супертипа обрабатываются что касается необработанных типов. В конструкторе класса звонки супер обрабатываются как вызовы метода на необработанном типе.


Обсуждение

Пример: Необработанные типы.

class Cell<E>
  E value;
  Cell (E v) { value=v; }
  A get() { return value; }
  void set(E v) { value=v; }
}
Cell x = new Cell<String>("abc");
x.value;          // OK, has type Object
x.get();          // OK, has type Object
x.set("def");     // unchecked warning


Обсуждение

Например,

import java.util.*;

class NonGeneric {

    Collection<Number> myNumbers(){return null;}
}
abstract class RawMembers<T> extends NonGeneric implements Collection<String> {
    static Collection<NonGeneric> cng = 
                                                                                new ArrayList<NonGeneric>();

    public static void main(String[] args) {
                RawMembers rw = null;
                Collection<Number> cn = rw.myNumbers(); // ok
                Iterator<String> is = rw.iterator(); // unchecked warning
                Collection<NonGeneric> cnn = rw.cng; // ok - static member
    }
}

RawMembers<T> наследовал метод

Iterator<String> iterator()
от Collection<String> суперинтерфейс. Однако, тип RawMembers наследовался iterator() от стирания его суперинтерфейса, что означает что тип возврата элемента iterator() стирание Iterator<<String>, Iterator. В результате попытка присвоиться к rw.iterator() требует преобразования непроверенного (§5.1.9) от Iterator к Iterator<String>, то, чтобы заставлять предупреждение непроверенное быть выпущенным.

Напротив, статический элемент cng сохраняет его полный параметризованный тип даже когда получено доступ через объект необработанного типа (отметьте, что доступ к статическому элементу через экземпляр считают плохим стилем и должен быть обескуражен). Элемент myNumbers наследован от NonGeneric (чье стирание также NonGeneric) и так сохраняет его полный параметризованный тип.



Обсуждение

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

Исторически, сырые данные вводят подстановочные знаки, которым предшествуют; они были сначала представлены в GJ, и описаны в производстве бумаги будущий сейф для прошлого: Добавляя Genericity к Языку программирования Java Gilad Bracha, Мартином Одерским, Дэвидом Стутэмайром, и Филипом Уодлером, в Proc. Конференции ACM по Объектно-ориентированному программированию, Системам, Языкам и Приложениям, (OOPSLA 98) октябрь 1998.


4.9 Перекрестные Типы

Перекрестный тип принимает форму T1 &... & Tn, n> 0, где Ti, 1 дюйм, являются выражениями типа. Перекрестные типы возникают в процессах преобразования получения (§5.1.10) и вывод типа (§15.12.2.7). Не возможно записать перекрестный тип непосредственно как часть программы; никакой синтаксис не поддерживает это. Значения перекрестного типа являются теми объектами, которые являются значениями всех типов Ti для 1 дюйма.

Элементы перекрестного типа T1 &... & Tn определяются следующим образом:


Обсуждение

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

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


4.10 Выделения подтипов

Подтип и отношения супертипа являются бинарными отношениями на типах. Супертипы типа получаются рефлексивным и переходным закрытием по прямому отношению супертипа, записанный S> 1 T, который определяется правилами, данными позже в этом разделе. Мы пишем S:> T, чтобы указать, что отношение супертипа содержит между S и T. S является надлежащим супертипом T, записанный T <S, если S:> T и S T.

Подтипы типа T являются всеми типами U так, что T, супертип U, и нулевой тип. Мы пишем T <: S, чтобы указать то, что это отношение подтипа содержит между типами T и S. T, является надлежащим подтипом S, записанный T <S, если T <:S и S T. T является прямым подтипом S, записанный T <1 S, если S> 1 T.

Выделение подтипов не расширяется через универсальные типы: T <: U не подразумевает что C <T> <: C <U>.

4.10.1 Выделение подтипов среди Типов примитивов

Следующие правила определяют прямое отношение супертипа среди типов примитивов:

двойной> 1 плавание
плавание> 1 долго
долго> 1 интервал
интервал> 1 случайная работа
интервал> 1 короткий
короткий> 1 байт

4.10.2 Выделение подтипов среди Класса и Интерфейсных Типов

Позвольте C быть описанием типа (§4.12.6, §8.1, §9.1) с нулем или большим количеством параметров типа (§4.4) F1..., Fn, у которых есть соответствующие границы B1..., Миллиард. То описание типа определяет ряд параметризованных типов (§4.5) C2 <T1..., Tn>, где каждый тип параметра, Ti передвигается на все типы, которые являются подтипами всех типов, перечисленных в связанном соответствии. Таким образом, для каждого связанного Си типа в висмуте Ti является подтипом Сайа [F1: = T1..., Fn: = Tn].

Учитывая описание типа для C <F1..., Fn>, прямые супертипы параметризованного типа (§4.5) C <F1..., Fn> являются всем следующим:

Прямые супертипы типа C <T1..., Tn>, где Ti, 1 дюйм, является типом, являются D <тета U1..., Британская тета>, где

Прямые супертипы типа C <R1..., Rn>, где по крайней мере один из Ri, 1 дюйм, является подстановочным параметром типа, являются прямыми супертипами C <X1..., Xn>, где

C <X1..., Xn> является результатом применения преобразования получения (§5.1.10) к C <R1..., Rn>.

Прямые супертипы перекрестного типа (§4.9) T1 &... & Tn, Ti, 1 дюйм.

Прямые супертипы переменной типа (§4.4) являются типами, перечисленными в его связанном.

Прямые супертипы нулевого типа являются всеми ссылочными типами кроме нулевого типа непосредственно.

В дополнение к вышеупомянутым правилам переменная типа является прямым супертипом своей нижней границы.

4.10.3 Выделение подтипов среди Типов Массива

Следующие правила определяют прямое отношение подтипа среди типов массива:

4.11, Где Типы Используются

Типы используются, когда они появляются в объявлениях или в определенных выражениях.

Следующий фрагмент кода содержит один или более экземпляров большинства видов использования типа:

import java.util.Random;
class MiscMath<T extends Number>{
        int divisor;
        MiscMath(int divisor) {
                this.divisor = divisor;
        }
        float ratio(long l) {
                try {
                        l /= divisor;
                } catch (Exception e) {
                        if (e instanceof ArithmeticException)
                                l = Long.MAX_VALUE;
                        else
                                l = 0;
                }
                return (float)l;
        }
        double gausser() {
                Random r = new Random();
                double[] val = new double[2];
                val[0] = r.nextGaussian();
                val[1] = r.nextGaussian();
                return (val[0] + val[1]) / 2;
    }
   Collection<Number> fromArray(Number[] na) {
           Collection<Number> cn = new ArrayList<Number>();
           for (Number n : na) {
                   cn.add(n)
           }
           return cn;
   }
   void <S> loop(S s){ this.<S>loop(s);}
           

}
В этом примере типы используются в объявлениях следующего:

и в выражениях следующих видов:

. Типы также используются в качестве параметров параметризованным типам; здесь тип Number используется в качестве параметра в параметризованном типе Collection<Number>.

4.12 Переменные

Переменная является местом хранения и имеет связанный тип, иногда называемый его типом времени компиляции, который является или типом примитива (§4.2) или ссылочным типом (§4.3). Значение переменной изменяется присвоением (§15.26) или префиксом или постфиксом ++ (инкремент) или -- (декрементный) оператор (§15.14.2, §15.14.3, §15.15.1, §15.15.2).

Совместимость значения переменной с ее типом гарантируется проектом языка программирования Java, пока программа не дает начало предупреждениям непроверенным (§4.12.2.1). Значения по умолчанию являются совместимыми (§4.12.5), и все присвоения на переменную проверяются на совместимость присвоения (§5.2), обычно во время компиляции, но, в единственных массивах включения случая, проверка на этапе выполнения делается (§10.10).

4.12.1 Переменные Типа примитива

Переменная типа примитива всегда содержит значение того точного типа примитива.

4.12.2 Переменные Ссылочного типа

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


Обсуждение

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


Если T является типом примитива, то переменная типа "массив T" может содержать нулевую ссылку или ссылку на любой массив типа "массив T"; если T является ссылочным типом, то переменная типа "массив T" может содержать нулевую ссылку или ссылку на любой массив типа "массив S" так, что, тип S является подклассом или подынтерфейсом типа T. Кроме того, переменная типа Object[] может содержать массив любого ссылочного типа. Переменная типа Object может содержать нулевую ссылку или ссылку на любой объект, ли экземпляр класса или массив.

4.12.2.1 Загрязнение "кучи"

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


Обсуждение

Например, код:

List l = new ArrayList<Number>();
List<String> ls = l; // unchecked warning
дает начало предупреждению непроверенному, потому что не возможно установить, любой во время компиляции (в рамках правил проверки типа времени компиляции) или во время выполнения, обращается ли переменная l действительно к a List<String>.

Если код выше выполняется, загрязнение "кучи" возникает, как переменная ls, объявленный быть a List<String>, обращается к значению, которое не является фактически a List<String>.

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

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

Код выше поэтому очень нетипичен, и действительно очень плохой стиль.

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

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

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


Переменная будет всегда обращаться к объекту, который является экземпляром класса, который реализует параметризованный тип.


Обсуждение

Например, значение l в примере выше всегда a List.


4.12.3 Виды Переменных

Есть семь видов переменных:

  1. Переменная класса является полем, объявил использование ключевого слова static в пределах объявления класса (§8.3.1.1), или с или без ключевого слова static в пределах интерфейсного объявления (§9.3). Переменная класса создается, когда ее класс или интерфейс готовятся (§12.3.2) и инициализируются к значению по умолчанию (§4.12.5). Переменная класса эффективно прекращает существование, когда ее класс или интерфейс разгружаются (§12.7).
  2. Переменная экземпляра является полем, объявленным в пределах объявления класса, не используя ключевое слово static (§8.3.1.1). Если у класса T есть поле a, которое является переменной экземпляра, то новая переменная экземпляра создаваемого и инициализированного к значению по умолчанию (§4.12.5) как часть каждого недавно создаваемого объекта класса T или любого класса, который является подклассом T (§8.1.4). Переменная экземпляра эффективно прекращает существование, когда, объектом которого это - поле, больше не ссылается, после любого необходимого завершения объекта был завершен (§12.6).
  3. Компоненты массива являются неназванными переменными, которые создаются и инициализируются к значениям по умолчанию (§4.12.5) всякий раз, когда новый объект, который является массивом, создается (§15.10). Компоненты массива эффективно прекращают существование, когда на массив больше не ссылаются. См. §10 для описания массивов.
  4. Параметры метода (§8.4.1) называют значения аргументов переданными к методу. Для каждого параметра, объявленного в объявлении метода, новая переменная параметра создается каждый раз, когда метод вызывается (§15.12). Новая переменная инициализируется с соответствующим значением аргумента от вызова метода. Параметр метода эффективно прекращает существование, когда выполнение тела метода полно.
  5. Параметры конструктора (§8.8.1) называют значения аргументов переданными конструктору. Для каждого параметра, объявленного в объявлении конструктора, новая переменная параметра создается каждый раз, когда выражение создания экземпляра класса (§15.9) или явный вызов конструктора (§8.8.7) вызывает того конструктора. Новая переменная инициализируется с соответствующим значением аргумента от выражения создания или вызова конструктора. Параметр конструктора эффективно прекращает существование, когда выполнение тела конструктора полно.
  6. Параметр обработчика исключений создается каждый раз, когда исключение поймано a catch пункт a try оператор (§14.20). Новая переменная инициализируется с фактическим объектом, связанным с исключением (§11.3, §14.18). Параметр обработчика исключений эффективно прекращает существование когда выполнение блока, связанного с catch пункт полон.
  7. Локальные переменные объявляются операторами объявления локальной переменной (§14.4). Всякий раз, когда поток управления вводит блок (§14.2) или for оператор (§14.14), новая переменная создается для каждой локальной переменной, объявленной в операторе объявления локальной переменной, сразу содержавшем в пределах того блока или for оператор. Оператор объявления локальной переменной может содержать выражение, которое инициализирует переменную. Локальная переменная с выражением инициализации не инициализируется, однако, до оператора объявления локальной переменной, который объявляет, что это выполняется. (Правила определенного присвоения (§16) препятствуют тому, чтобы значение локальной переменной использовалось прежде, чем это было инициализировано или иначе присвоило значение.) Локальная переменная эффективно прекращает существование когда выполнение блока или for оператор полон.

Было это не для одной исключительной ситуации, локальная переменная могла всегда расцениваться как создаваемый, когда ее оператор объявления локальной переменной выполняется. Исключительная ситуация включает switch оператор (§14.11), где для управления возможно ввести блок, но обходное выполнение оператора объявления локальной переменной. Из-за ограничений, введенных правилами определенного присвоения (§16), однако, не может использоваться локальная переменная, объявленная таким обошедшим оператором объявления локальной переменной прежде, чем это было определенно присвоено значение выражением присвоения (§15.26).

Следующий пример содержит несколько различных видов переменных:

class Point {
        static int numPoints;                   // numPoints is a class variable
        int x, y;                               // x and y are instance variables
        int[] w = new int[10];                  // w[0] is an array component
        int setX(int x) {                       // x is a method parameter
                int oldx = this.x;              // oldx is a local variable
                this.x = x;
                return oldx;
        }
}

4.12.4 заключительные Переменные

Переменная может быть объявлена final. Заключительная переменная может только быть присвоена однажды. Это - ошибка времени компиляции, если заключительная переменная присваивается тому, если это не является определенно неприсвоенным (§16) сразу до присвоения.

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

Однажды a final переменная была присвоена, она всегда содержит то же самое значение. Если a final переменная содержит ссылку на объект, тогда состояние объекта может быть изменено операциями на объекте, но переменная будет всегда обращаться к тому же самому объекту. Это применяется также массивам, потому что массивы являются объектами; если a final переменная содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями на массиве, но переменная будет всегда обращаться к тому же самому массиву.

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

В примере:

class Point {
        int x, y;
        int useCount;
        Point(int x, int y) { this.x = x; this.y = y; }
        final static Point origin = new Point(0, 0);
}
класс Point объявляет заключительную переменную класса origin. origin переменная содержит ссылку на объект, который является экземпляром класса Point чьи координаты (0, 0). Значение переменной Point.origin никогда не может изменяться, таким образом, это всегда обращается к тому же самому Point объект, тот создается его инициализатором. Однако, работа на этом Point объект мог бы изменить свое состояние например, изменяя его useCount или даже, обманчиво, x или y координата.

Мы вызываем переменную типа примитива или типа String, это является заключительным и инициализируется с константным выражением времени компиляции (§15.28) постоянная переменная. Является ли переменная постоянной переменной или не может иметь импликации относительно инициализации класса (§12.4.1), совместимость на уровне двоичных кодов (§13.1, §13.4.9) и определенное присвоение (§16).

4.12.5 Начальные значения Переменных

У каждой переменной в программе должно быть значение прежде, чем его значение будет использоваться:

Пример программы:

class Point {
        static int npoints;
        int x, y;
        Point root;
}
class Test {
        public static void main(String[] args) {
                System.out.println("npoints=" + Point.npoints);
                Point p = new Point();
                System.out.println("p.x=" + p.x + ", p.y=" + p.y);
                System.out.println("p.root=" + p.root);
        }
}
печатные издания:

npoints=0
p.x=0, p.y=0
p.root=null
иллюстрирование инициализации по умолчанию npoints, который происходит когда класс Point готовится (§12.3.2), и инициализация по умолчанию x, y, и root, который происходит когда новое Point инстанцируется. См. §12 для полного описания всех аспектов загрузки, соединения, и инициализации классов и интерфейсов, плюс описание инстанцирования классов, чтобы сделать новые экземпляры класса.

4.12.6 Типы, Классы, и Интерфейсы

В языке программирования Java, каждой переменной и каждом выражении имеет тип, который может быть определен во время компиляции. Тип может быть типом примитива или ссылочным типом. Ссылочные типы включают типы классов и интерфейсные типы. Ссылочные типы представляются описаниями типа, которые включают объявления класса (§8.1) и интерфейсные объявления (§9.1). Мы часто используем термин тип, чтобы обратиться к классу или к интерфейсу.

Каждый объект принадлежит некоторому определенному классу: класс, который был упомянут в выражении создания, которое произвело объект, класс чей Class объект использовался, чтобы вызвать отражающий метод, чтобы произвести объект, или String класс для объектов неявно создается оператором конкатенации строк + (§15.18.1). Этот класс вызывают классом объекта. (У массивов также есть класс, как описано в конце этого раздела.) Объект, как говорят, является экземпляром своего класса и всех суперклассов его класса.

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

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

Даже при том, что у переменной или выражения может быть тип времени компиляции, который является интерфейсным типом, нет никаких экземпляров интерфейсов. Переменная или выражение, тип которого является интерфейсным типом, могут сослаться на любой объект, класс которого реализует (§8.1.5) тот интерфейс.

Вот пример создания новых объектов и различия между типом переменной и классом объекта:

public interface Colorable {
        void setColor(byte r, byte g, byte b);
}
class Point { int x, y; }
class ColoredPoint extends Point implements Colorable {
        byte r, g, b;
        public void setColor(byte rv, byte gv, byte bv) {
                r = rv; g = gv; b = bv;
        }
}
class Test {
        public static void main(String[] args) {
                Point p = new Point();
                ColoredPoint cp = new ColoredPoint();
                p = cp;
                Colorable c = cp;
        }
}
В этом примере:


Обсуждение

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


У каждого массива также есть класс; метод getClass, когда вызвано для объекта массива, возвратит объект класса (класса Class) это представляет класс массива.

У классов для массивов есть странные имена, которые не являются допустимыми идентификаторами; например, класс для массива int у компонентов есть имя"[I"и так значение выражения:

new int[10].getClass().getName()
строка "[I"; см. спецификацию Class.getName для деталей.


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

Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу форму обратной связи

free hit counter