Чтение онлайн

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

• Загрузка — выполняется загрузчиком классов. Последний находит байт-код и создает на его основе объект Class.

• Компоновка — в фазе компоновки проверяется байт-код класса, выделяется память для статических полей, и при необходимости разрешаются все ссылки на классы, созданные этим классом.

• Инициализация — если у класса имеется суперкласс, происходит его инициализация, выполняются статические инициализаторы и блоки статической инициализации.

Инициализация откладывается до первой ссылки на статический метод (конструкторы являются статическими

методами) или на неконстантное статическое поле:

//: typeinfo/Class Initialization.java import java util *;

class Initable {

static final int staticFinal = 47; static final int staticFinal2 =

Classlnitialization rand nextlnt(lOOO).

static {

System out рпп^пС'Инициализация Initable"), продолжение &

}

}

class Initable2 {

static int staticNonFinal = 147, static {

System out.рпп^пСИнициализация Initable2"),

}

}

class Initable3 {

static int staticNonFinal = 74. static {

System ои^ргШ1п("Инициализация Initable3").

}

}

public class Classlnitialization {

public static Random rand = new Random(47), public static void main(String[] args) throws Exception { Class initable = Initable class. System out printin("После создания ссылки Initable"). // He приводит к инициализации System out println(Initable.staticFinal). // Приводит к инициализации-System out printin(Initable staticFinal2); // Приводит к инициализации System out println(Initable2 staticNonFinal). Class initable3 = Class forName("Initable3"). System out printlnC"После создания ссылки Initable3"). System out pri ntin(Initable3 staticNonFinal),

}

} /* Output

После создания ссылки Initable 47

Инициализация Initable 258

Инициализация Ini table2 147

Инициализация Initable3 После создания ссылки Ini tablеЗ 74 *///•-

По сути, инициализация откладывается настолько, насколько это возможно. Из результатов видно, что простое использование синтаксиса .class для получения ссылки на класс не приводит к выполнению инициализации. С другой стороны, вызов Class.forNames немедленно инициализирует класс для получения ссылки на Class, как мы видим на примере initable3.

Параметризованные ссылки

Объект Class используется для создания экземпляров класса и содержит полный код методов этих экземпляров. Кроме того, в нем содержатся статические члены класса. Таким образом, ссылка на Class подразумевает точный тип того, на что она указывает — на объект класса Class.

Однако проектировщики Java SE5 решили предоставить возможность уточнения записи посредством ограничения типа объекта Class, на который может указывать ссылка; для этой цели применяется синтаксис параметризации. В следующем примере верны оба варианта синтаксиса:

// typeinfo/GenericClassReferences java

public class GenericClassReferences {

public static void main(String[] args) { Class intClass = int.class, Class<Integer> genericIntClass = int class; genericIntClass = Integer class; // To же самое intClass = double.class,

// genericIntClass = double class; // Недопустимо

}

} ///;-

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

Новый синтаксис преобразования

В Java SE5 также появился новый синтаксис преобразования ссылок

на Class, основанный на методе cast:

II typeinfo/ClassCasts java

class Building {}

class House extends Building {}

public class ClassCasts {

public static void main(String[] args) { Building b = new HouseO; Class<House> houseType = House class, House h = houseType cast(b); h = (House)b; II . А можно и так.

}

} III ~

Метод cast получает объект-аргумент и преобразует его к типу ссылки на Class. Конечно, при взгляде на приведенный код может показаться, что он занимает слишком много места по сравнению с последней строкой main, которая делает то же самое. Новый синтаксис преобразования полезен в тех ситуациях, когда обычное преобразование невозможно. Обычно это происходит тогда, когда при написании параметризованного кода (см. далее) ссылка на Class сохраняется для преобразования в будущем. Подобные ситуации встречаются крайне редко — во всей библиотеке Java SE5 cast используется всего один раз (в com.sun.mirror.uti'LDeclarationFilter).

Другая новая возможность — Class.asSubclass — вообще не встречается в библиотеке Java SE5. Этот метод позволяет преобразовать объект класса к более конкретному типу.

Проверка перед приведением типов

Итак, мы рассмотрели следующие формы RTTI:

• Классическое преобразование; аналог выражения «(Shape)», которое проверяет, «законно» ли приведение типов в данной ситуации, и в случае неверного преобразования возбуждает исключение ClassCastException.

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

В языке С++ классическая форма типа «(Shape)» вообще не задействует RTTI. Она просто сообщает компилятору, что необходимо обращаться с объектом как с новым типом. В языке Java, который при приведении проверяет соответствие типов, такое преобразование часто называют «безопасным нисходящим приведением типов». Слово «нисходящее» используется в силу традиций, сложившихся в практике составления диаграмм наследования. Если приведение окружности Circle к фигуре Shape является восходящим, то приведение фигуры Shape к окружности Circle является, соответственно, нисходящим. Поскольку компилятор знает, что Circle является частным случаем Shape, он позволяет использовать «восходящее» присваивание без явного преобразования типа. Тем не менее, получив некий объект Shape, компилятор не может быть уверен в том, что он получил: то ли действительно Shape, то ли один из производных типов (Circle, Square или Triangle). На стадии компиляции он видит только Shape и поэтому не позволит использовать «нисходящее» присваивание без явного преобразования типа.

Существует и третья форма RTTI в Java — ключевое слово instanceof, которое проверяет, является ли объект экземпляром заданного типа. Результат возвращается в логическом (boolean) формате, поэтому вы просто «задаете» вопрос в следующей форме:

if(x instanceof Dog) ((Dog)x).bark.

Команда if сначала проверяет, принадлежит ли объект х классу Dog, и только после этого выполняет приведение объекта к типу Dog. Настоятельно рекомендуется использовать ключевое слово instanceof перед проведением нисходящего преобразования, особенно при недостатке информации о точном типе объекта; иначе возникает опасность исключения ClassCastException.

Поделиться с друзьями: