Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 72 73 74 75 76 77 78 79 80 ... 132

Однако проектировщики 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.

Обычно проводится поиск одного определенного типа (например, поиск треугольников среди прочих фигур), но с помощью ключевого слова instanceof легко можно идентифицировать все типы объекта. Предположим, что у нас есть иерархия классов для описания домашних животных Pet (и их владельцев — эта особенность пригодится нам в более позднем примере). Каждое существо (Individual) в этой иерархии обладает идентификатором id и необязательным

именем. В данный момент код Individual нас не интересует — достаточно знать, что объект можно создавать с именем или без, и у каждого объекта Individual имеется метод id(), возвращающий уникальный идентификатор. Также имеется метод toString(); если имя не указано, toStringO выдает имя типа. Иерархия классов, производных от Individual:

// typeinfo/pets/Person.java package typeinfo.pets;

public class Person extends Individual {

public Person(String name) { super(name), } } III:-

//: typeinfo/pets/Pet.java package typeinfo pets;

public class Pet extends Individual {

public Pet(String name) { super(name), } public Pet О { superO. } } ///.-

//: typeinfo/pets/Dog.java package typeinfo.pets,

public class Dog extends Pet {

public Dog(String name) { super(name), } public Dog О { superO, } } Hill: typeinfo/pets/Mutt java package typeinfo pets;

public class Mutt extends Dog {

public Mutt(String name) { super(name); } public Mutt О { superO; } } ///.-

II: typeinfo/pets/Pug java package typeinfo pets;

public class Pug extends Dog {

public Pug(String name) { super(name); } public PugO { superO; } } Hill. typeinfo/pets/Cat java package typeinfo pets,

public class Cat extends Pet {

public Cat(String name) { super(name), } public CatO { superO, } } lll-

IH typeinfo/pets/EgyptianMau java package typeinfo.pets;

public class EgyptianMau extends Cat { продолжение &

public EgyptianMau(String name) { super(name); } public EgyptianMau() { superO; } } /// -

// typeinfo/pets/Manx java package typeinfo pets,

public class Manx extends Cat {

public Manx(String name) { super(name); } public ManxO { superO; }

} 111

II typeinfo/pets/Cymric java package typeinfo pets,

public class Cymric extends Manx {

public Cymric(String name) { super(name). } public CymricO { superO; } } III -

II. typeinfo/pets/Rodent java package typeinfo pets,

public class Rodent extends Pet {

public Rodent(String name) { super(name), } public RodentO { superO; } } III -

II' typeinfo/pets/Rat java package typeinfo pets,

public class Rat extends Rodent {

public Rat(String name) { super(name); } public RatO { superO; } } Hill' typeinfo/pets/Mouse java package typeinfo pets;

public class Mouse extends Rodent {

public Mouse(String name) { super(name); } public MouseO { superO, } } ///:-

II typeinfo/pets/Hamster java package typeinfo pets.

public class Hamster extends Rodent {

public Hamster(String name) { super(name); } public HamsterO { superO; } } ///:-

Затем нам понадобятся средства для создания случайных типов Pet, а для удобства — массивов и списков (List) с элементами Pet. Чтобы этот инструментарий мог «пережить» несколько разных реализаций, мы определим его в виде абстрактного класса:

// typeinfo/pets/PetCreator java // Создание случайных последовательностей Pet package typeinfo pets, import java util *,

public abstract class PetCreator {

private Random rand = new Random(47), // Список создаваемых типов, производных от Pet public abstract List<Class<? extends Pet» typesO. public Pet randomPetO { // Создание одного случайного объекта Pet int n = rand next Int (types О sizeO), try {

return typesO get(n) newInstanceO. } catchdnstantiationException e) {

throw new RuntimeException(e), } catchdllegalAccessException e) {

throw new RuntimeException(e).

}

}

public Pet[] createArray(int size) { Pet[] result = new Pet[size], for(int i = 0. i < size, i++)

result[i] = randomPetO. return result;

}

public ArrayList<Pet> arrayList(int size) {

ArrayList<Pet> result = new ArrayList<Pet>(), Collections addAll(result, createArray(size)), return result,

}

} /// ~

Абстрактный метод getTypes() поручает производному классу получение списка объектов Class. В качестве типа класса указан «любой производный от Pet», поэтому newlnstance() создает Pet без необходимости преобразования типа. Метод randomPet() осуществляет случайную выборку из List и использует полученные объекты Class для создания нового экземпляра данного класса вызовом Class. newlnstance(). Метод createArray() использует randomPet() для заполнения массива, a arrayList(), в свою очередь, использует createArray().

При вызове newlnstance() возможны два вида исключений, обрабатываемые в секциях catch за блоком try. Имена исключений достаточно хорошо объясняют суть проблемы (IllegalAccessException — нарушение механизма безопасности Java, в данном случае если конструктор по умолчанию объявлен private).

Определяя субкласс PetCreator, достаточно предоставить список типов Pet, которые должны создаваться с использованием randomPet() и других методов. Метод getTypes() возвращает ссылку на статический объект List. Реализация с использованием forName() выглядит так:

//• typei nfo/pets/ForNameCreator.java package typeinfo pets; import java util *;

public class ForNameCreator extends PetCreator {

private static List<Class<? extends Pet» types =

new ArrayList<Class<? extends Pet»(). Л Л

продолжение &

II Типы, создаваемые случайным образом, private static StringE] typeNames = { "typeinfo pets.Mutt", "typeinfo pets Pug", "typeinfo pets EgyptianMau", "typeinfo pets.Manx", "typeinfo.pets.Cymric", "typeinfo.pets.Rat", "typeinfo.pets.Mouse", "typeinfo pets.Hamster"

}:

@SuppressWarni ngs("unchecked") private static void loaderO { try {

for(String name : typeNames) types.add(

(Class<? extends Pet>)Class forName(name)). } catch(ClassNotFoundException e) {

throw new RuntimeException(e);

}

}

static { loaderO; }

public List<Class<? extends Pet» types О {return types,} } ///:-

Метод loader() создает список List объектов Class с использованием метода Class.forName(). При этом может произойти исключение ClassNotFoundException, что вполне понятно — ведь ему передается строка, содержимое которой невозможно проверить на стадии компиляции. При ссылке на эти классы необходимо указывать имя пакета, которому они принадлежат (typeinfo).

1 ... 72 73 74 75 76 77 78 79 80 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий