Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Технологии обработки информации на Java. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
Для управления порядком выполнения программы используется ряд
инструкций, выражения в которых могут иметь только тип boolean:
Инструкция if Инструкция while Инструкция do Инструкция for
Выражение типа boolean также определяет, какое из подвыражений
будет вычисляться в условной операции ? :
Только boolean или Boolean выражения могут быть использованы в ин-
струкциях управления порядком выполнения программы, и как первый опе­ран
д в условной операции ? :.
Целочисленное или вещественное значение может быть сконвер­тировано к типу boolean, следуя соглашениям языка C, согласно которо­му любое ненулевое значение – это 0, путем выполнения выражения
x!=0.
Объектная ссылка obj может быть сконвертирована к типу Boolean, следуя соглашениям языка C, согласно которому любое отличное от null значение – это true, путем выполнения выражения obj!=0.
Значение типа boolean мож
ет быть сконвертировано к типу String пу-
тем строковой конверсии.
Разрешено приведение значения типа boolean к типам boolean или Boolean. Любые другие преобразования над типом boolean запрещены.
2.3. Ссылочные Типы и Значения
В языке программирования Java выделяются четыре ссылочных ти­па: классы, интерфейсы, типовые переменные и массивы.
СсылочныйТип:
КлассИлиИнтерфейс
ТиповаяПеременная
Массив
КлассИлиИнтерфейс:
Класс
Интерфейс
Класс:
УказаниеТипа АргументыТипа
Интерфейс:
УказаниеТипа АргументыТипа
УказаниеТипа:
ИмяТипа
КлассИлиИнтерфейс . Идентификатор
ИмяТипа:
опц
опц
21
Идентификатор
ИмяТипа . Идентификатор
ТиповаяПеременная:
Идентификатор
Массив:
Тип [ ]
Рассмотрим образец кода:
class Point { int [] metrics; } interface Move { void move( int deltax, int delty);}
В данном коде объявлен класс Point, интерфейс Move, и при объявле-
нии поля metrics используется массив int [] (массив переменных типа int).
Для того чтобы использовать в каком-либо месте такой ссылочный тип, как класс или интерфейс, необходимо указание типа, за которым мо­гут следовать (или нет) аргументы типа. Если аргументы типа появляются где-либо в указании класса или интерфейса, то такой тип называется пара­метризованным.
Указание типа может состоять либо из одного имени типа, либо же из имени типа (класс или интерфейс), за которым следует знак “.” и далее следует идентификатор. В формальном виде ук в форме T.id, где id должен быть простым именем доступного членского типа T, иначе происходит ошибка компиляции. Такое указание обозначает этот членский тип.
В языке программирования Java существуют такие контексты, в которых гене­рик классы или интерфейсы используются без указания аргументов типа. Под этими контекстами не подразумеваются ситуации с использованием необработанных типов. Здесь, скорее, имеются в виду такие ситуации, когда аргументы типа необязательны или неважны для значения генерик класса или интерфейса.
К примеру, инструкция одиночного импортирования import java.util.List; вводит в область видимости модуля компиляции простое имя типа List так, что теперь может использоваться его параметризованная форма List<…>. В качестве другого примера можно привести вызов статического метода генерик класса, для чего требуется исполь­зование только имени (возможно, квалифицированного) генерик класса, без указания аргументов типа, так как с точки зрения статического метода они не имеют смысла (хо­тя сам по себе метод может быть генериковым и принимать свои собственные типовые аргументы, но при этом они никоим образом не обязаны быть связаны с типовыми па­раметрами содержащего его класса.
Ввиду возникающей необходимости использовать генерик классы или интре­фейсы без указания типовых аргументов, имена типов и указание типа – это не одно и тоже. Имя типа всегда указывается через имя другого типа. В некоторых случаях необходимо получить доступ к внутреннему классу, который является членом парамет­ризованного типа.
Далее приводится пример, в котором указание типа отделено от имени типа:
азание типа осуществляется
22
class GenericOuter <T extends Number>{
public class Inner <S extends Comparable<S>>{ T getT(){ return null;} S getS(){ return null;} }
}
class Test { public static void main(String[] args) { GenericOuter<Integer>.Inner<Double> x1 =null; Integer I = x1.getT(); Double d = x1.getS(); }
}
В том случае, если бы мы осуществляли доступ к классу Inner, исполь-
зуя имя типа, например:
GenericOuter.Inner x1 =null;
то мы настояли бы на использовании необработанного типа, теряя при
этом информацию о типовой переменной.
2.3.1. Объекты
Объект – это экземпляр класса или массива.
Ссылочные значения (обычно говорят просто ссылки) – это указате­ли на такие объекты, и в дополнение к ним существует специальное null значение, которое указывает на отсутствие какой-либо ссылки.
Экземпляр класса может быть явно создан с использованием выра­жения создания экземпляра класса.
Экземпляр массива может быт
ь явно создан с использованием выра-
жения создания экземпляра массива.
В результате выполнения операции строковой конкатенации + ис­пользованной в неконстантном выражении, неявно создается новый объект типа String.
Новый экземпляр массива неявно создается при выполнении выра­жения инициализации массива; это может произойти при инициализации класса или интерфейса, при создании нового экземпляра класса, или когда выполняется объявление новой локальной переменной.
Новые объекты типа Boolean, Byte, Short, Character, Integer, Long, Float и Double могут быть неявно созданы при выполнении боксинга.
Пример 2.3.1-1. Создание объекта
class Point {
int x, y; Point() { System.out.println("default"); } Point(int x, int y) { this.x = x; this.y = y; } /* Экземпляр объекта Point явно создается во время
инициализации класса */
23
static Point origin = new Point(0,0); /* Объект типа String может быть неявно создан с по-
мощью операции конкатенации + */
public String toString() { return "(" + x + "," + y + ")"; }
}
class Test { public static void main(String[] args) {
/* Объект класса Point явно создается с помощью ре-
флексивного метода newInstance: */
Point p = null; try { p = (Point)Class.forName("Point").newInstance(); } catch (Exception e) { System.out.println(e); } /* Неявное создание объекта массива с использованием
конструктора массива: */
Point a[] = { new Point(0,0), new Point(1,1) }; /* Объекты типа String неявно создаются с помощью
операции +: */
System.out.println("p: " + p); System.out.println("a: { " + a[0] + ", " + a[1] + "
}");
/* Явное создание массива с использованием выражения
создания массива: */
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
Над ссылочными типами допустимы следующие операции:
доступ к полям с использованием квалифицированного имени или выражения доступа к полю;
вызов метода;
оператор приведения;
оператор строковой конкатенации +, который выполняется в том
случае, если один из операндов имеет тип String, а второй имеет ссылоч­ный тип. В этом случае ссылочный операнд преобразуется к типу String
24
путем вызова у объекта метода toString (при этом, если сама ссылка имеет значение null, или метод toString возвращает null, то результатом будет строка «null»), и затем создаст новый объект типа String, который будет конкатенацией этих двух строк;
операция instanceof;
операция ссылочной эквивалентности == и !=
условная операция ? :
На один и тот же объект может существовать множество ссылок. Большинство объектов имеют состояние, хранящееся в полях объекта, ко­торые являют
ся экземплярами классов или в переменных, которые явля­ются компонентами массивов. Если две переменные содержат ссылки на один и тот же объект, состояние объекта может быть изменено с использо­ванием одной из ссылок, и произошедшие изменения будут доступны для использования через другую ссылку.
Пример 2.3.1-2. Примитивная и Ссылочная идентичность
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(" но 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(" и v2.val==" + v2.val); } }
Результатом копиляции и выполнения программы будут следующие строки:
i1==3 но i2==4
v1.val==6 и v2.val==6
так как v1.val и v2.val указывают на одно и то же поле объекта в одном и том же объекте Value, созданным одним и тем же выражением new, в то вре­мя как i1 и i2 это различные переменные.
Каждый объект ассоциирован с монитором, который используется методом synchronized и инструкцией synchronized для предоставления контроля над совместным доступом к состоянию объектов у нескольких потоков.
25
2.3.2. Класс Object
Класс Object – это суперкласс для всех остальных классов.
Все классы и массивы наследуют методы класса Object, а он имеет следующие методы:
Метод clone, используется для создания копии объекта.
Метод equals, определяющий понятие объектного равенства, ос-
новываясь не на ссылках, а на значениях, определяющих состояние объ­екта.
Метод finalize, который должен быть вызван прямо перед тем, ка
к
объект будет удален из памяти.
Метод getClass, который возвращает объект типа Class, который представляет класс объекта.
Объект типа Class существует для каждого ссылочного типа. Он мо­жет быть использован, к примеру, для обнаружения полного квалифициро­ванного имени класса, его членов, его непосредственного суперкласса, и любых интерфейсов, которые он реализует.
Выражен
ие вызова метода getClass имеет тип Class<? extends |T|>,
где T – это класс или интерфейс, искомый для метода getClass.
Метод класса, который объявляет инструкцию synchronized, синхро­низируется с использованием мониторов, ассоциированных с объектом Class данного класса.
Метод hashCode, который крайне полезен в комбинации с методом equals при использовании, например, в хэштаблицах, таких как ja­va.util.Hashmap.
Методы wait, notify и notifyAll, используются в многопоточном программировании с использованием тре
дов.
Метод toString, который возвращает строковое представление объ- екта в виде объекта класса String.
2.3.3. Класс String
Объект класса String представляет собой последовательность кодо­вых точек Unicode.
Объект класса String имеет константное (неизменяемое) значение.
Строковые литералы являются ссылками на объекты класса String.
Оператор строковой конкатенации + неявно создает новый объект класса String в тех случаях, когда результат был получен с использованием неконстантного выражения во время исполнения программы.
2.3.4. Когда Ссылочные типы являются Одними и теми же
Два ссылочных типа имеют один и тот же тип времени компиляции в том случае, если они имеют одинаковое бинарное имя, и их типовые ар­гументы, если они есть, тоже одинаковы, применяя данное определение рекурсивно.
26
В том случае, если два ссылочных типа являются одним и тем же ти­пом, о них иногда говорят как об одинаковых классах или одинаковых ин­терфейсах.
Во время исполнения программы несколькими разными загрузчика­ми классов могут быть одновременно загружены несколько ссылочных ти­пов с одним и тем же бинарным именем. В то же время эти типы могут представлять ка
к один и тот же, так и разные типы. Однако даже если два
таких типа представляют один и тот же тип, они считаются разными.
Два ссылочных типа считаются одинаковыми во время исполнения программы, если:
они оба классы или интерфейсы, определенные одним и тем же загрузчиком классо
в, и имеют одно и то же бинарное имя, в каковом слу­чае они называются одинаковыми классами времени исполнения, либо же одинаковыми интерфейсами времени исполнения;
они оба массивы, и типы их компонент имеют одинаковый тип
времени исполнения.
2.4. Типовые переменные
Типовая переменная – это неквалифицированный идентификатор, используемый в качестве типа в классе, интерфейсе, методе и конструк­торе.
Типовая переменная объявляется как типовый параметр объявления генерик класса, объявления генерик интерфейса, объявления генерик ме­тода или объявления генерик конструктора.
ТиповыйПараметр:
ТиповаяПеременная ГраницаТипа
опц
ГраницаТипа:
extends ТиповаяПеременная extends КлассИлиИнтерфейс СписокДополнительныхГраниц
опц
СписокДополнительныхГраниц:
ДополнительнаяГраница СписокДополнительныхГраниц ДополнительнаяГраница
ДополнительнаяГраница:
& ИнтерфейсныйТип
Каждая типовая переменная, объявленная как типовый параметр, имеет границу. Если для типовой перменной не объявлена граница, то ей считается класс Object. Если граница объявлена, то она может быть одной из следующих:
27
одна типовая переменная T; или
класс или интерфейс T, за которым могут следовать интерфейсы
I
& … & In.
1
Во время компиляции исходного кода происходит ошибка, если лю­бой из типов I
& … & In – это класс или типовая переменная.
1
Типовая переменная не должна быть одновременно подтипом двух интерфейсов, являющихся разными параметризациями одного и того же генерик интерфейса, иначе происходит ошибка компиляции.
Порядок указания типов при определении границы значим только в том смысле, что стирание типов определяется первым типом в его грани­цах, а также, что клас
с или типовая переменная может появляться только
сразу после ключевого слова extends.
Члены типовой переменной X с границами T & I членами типового пересечения T & I
& … & In, появляющегося там, где
1
& … & In являются
1
объявлена типовая переменная.
Пример 2.4-1. Члены Типовых Переменных
package TypeVarMembers;
class C {
public void mCPublic() {} protected void mCProtected() {} void mCDefault() {} private void mCPrivate() {}
}
interface I {
void mI();
}
class CT extends C implements I {
public void mI() {}
}
class Test {
<T extends C & I> void test(T t) { t.mI(); // OK t.mCPublic(); // OK t.mCProtected(); // OK t.mCDefault(); // OK t.mCPrivate(); // Ошибка компиляции }
}
Типовая переменная T имеет те же члены типа, что и типовое пересече­ние C&I, которое, в свою очередь, имеет те же члены, что и пустой класс CT, определенный в той же области видимости с эквивалентными супертипами.
28
Члены интрефейса всегда имеют модификатор public, и, таким образом, всегда наследуется (если только они не были переопределены). Следовательно, mI это одновременно и член классов CT и T. Среди членов класса С все методы, кроме mCPrivate, наследуются классом CT, и, таким образом, становятся чле­нами классов CT и T.
Если бы типовый аргумент С был бы объявлен в пакете, отличном от того, в котором объявлена типовая переменная T, тогда вызов метода mCDefault вызвал бы ошибку компиляции, так как этот член не был бы досту­пен в точке объявления типовой переменной T.
2.5. Параметризованные Типы
Объявление генерик класса или интерфейса С, который имеет один или более типовых параметров A1,…,An, каждый из которых, соответ­ственно, имеет границы B1,…,Bn, определяет возможный набор парамет­ризованных типов по одному на каждый возможный вызов секции типово­го параметра.
Каждый параметризованный тип в таком наборе имеет вид C<
,…,Tn >, где каждый типовый аргумент Ti может представлять собой все
T
1
типы и подтипы тех типов, которые перечислены в указанных границах. Таким образом, для каждой типовой границы S
, в Bi, Ti это подтип для
i
Si[F1:=T1,...,Fn:=Tn].
Параметризованный тип – это Класс или Интерфейс, который содер­жит, по меньшей мере, одно указание типа, после которого указан список типовых аргументов < T1,…,Tn >. Список типовых аргументов определяет конкретные типы, вызываемые для каждого типового параметра генерик типа, указанного в объявлении типа.
Пусть дано указание типа, за которым следует список типовых аргу­ментов, и пусть C будет завершенным Идентификатором этого указания.
Во время компиляции исходн
ого кода происходит ошибка, если С не является именем генерик класса или интерфейса, или если число аргумен­тов в списке типовых аргументов отличается от числа параметров в С.
Пусть P= C< T
,…,Tn > – это параметризованный тип. Тогда в ре-
1
зультате выполнения операции capture conversion над типом P должен получиться тип C< X
,…,X
1
> для каждого типового аргумента Xi (1 i
n
n), Xi <: Bi[A1:=X1,...,An:=Xn ], иначе произойдет ошибка
компиляции.
Используемая в данной спецификации нотация [Ai:=Ti] обозначает
замену типовой переменной A
типом Ti, где i находится в диапазоне 1
i
i n.
В данной спецификации, в тех случаях, когда речь идет о классе или интерфейсе, считается, что они могут быть как обычными, так и генерик типами, кроме тех случаев, где явно указано обратное.
29
Примеры параметризованных типов:
Vector<String>
Seq<Seq<A>>
Seq<String>.Zipper<Integer>
Collection<Integer>
Pair<String,String>
Примеры некорректных вызовов параметризованных типов:
Vector<int> – это неверно, т.к. примитивы не могут быть типовыми аргументами;
Pair<String> – это неверно, т.к. указано слишком мало типовых аргументов (надо два);
Pair<String,String,String>, т.к. указано слишком много типовых аргументов (надо два).
Параметризованный тип может быть вызовом генерик класса или интерфейса, которые являются нестед типами. К примеру, если не генерик класс C имеет членский генерик класс D<T>, тогда C.D<Object> – это параметризованный тип. И если генерик класс C<T> имеет членский не генерик класс D, тогда членский тип
C<String>.D – это тоже параметризованный тип, даже несмотря на то, что класс D – не генерик класс.
Два параметризованных типа являются различными, если выполня­ется одно из следующих условий:
они являются вызовом различных генерик классов;
их типовые аргументы различаются.
2.5.1. Типовые аргументы и Маски типов
Типовые аргументы могут быть как известными ссылочными типа­ми, так и представлять собой маски типов. Маски типов полезны в тех си­туациях, когда известн
а только часть информации о том типовом парамет-
ре, который потребуется в дальнейшем.
Типовые аргументы:
<СписокТиповыхАргументов>
СписокТиповыхАргументов:
ТиповыйАргумент СписокТиповыхАргументов, ТиповыйАргумент
ТиповыйАргумент:
СсылочныйТип МаскаТипа
МаскаТипа:
? ГраницыМаскиТипаопц
30
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]