Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Технологии обработки информации на Java. Учебное пособие
.pdf
Для управления порядком выполнения программы используется ряд
инструкций, выражения в которых могут иметь только тип 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 при использовании, например, в хэштаблицах, таких как java.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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
