Брюс Эккель - Философия Java3

Скачивание начинается... Если скачивание не началось автоматически, пожалуйста нажмите на эту ссылку.
Жалоба
Напишите нам, и мы в срочном порядке примем меры.
Описание книги "Философия Java3"
Описание и краткое содержание "Философия Java3" читать бесплатно онлайн.
Неограниченные метасимволы
Казалось бы, неограниченный метасимвол <?<@062> должен означать «все, что угодно», а его использование эквивалентно использованию низкоуровневого типа. В самом деле, на первый взгляд компилятор подтверждает эту оценку:
//: generics/UnboundedWi1dcardsl.java
import java.util.*;
public class UnboundedWildcardsl {
static List listl;
static List<?> list2;
static List<? extends Object> list3;
static void assignldist list) { listl = list; 1i st2 = list;
// list3 = list; // Предупреждение: непроверенное преобразование // Обнаружен List, требуется List<? extends Object>
}
static void assign2(List<?> list) { listl = list; list2 = list; list3 = list;
}
static void assign3(List<? extends Object> list) { listl = list; list2 = list; list3 = list;
}
public static void main(String[] args) { assignl(new ArrayListO): assign2(new ArrayList()); // assign3(new ArrayListO); // Предупреждение-// Непроверенное преобразование. Обнаружен- ArrayList // Требуется: List<? extends Object> assignl(new ArrayLi st<String>0); assign2(new ArrayList<String>0); assign3(new ArrayList<String>0); // Приемлемы обе формы-List<?> wildList = new ArrayListO; wildList = new ArrayList<String>(); assignl(wildList); assign2(wildList); assign3(wildLi st);
}
} ///:-
Во многих ситуациях, подобных рассмотренной, для компилятора совершенно не существенно, используется низкоуровневый тип или <?>. Конструкцию <?> можно считать обычным украшением; впрочем, она обладает некоторой практической ценностью, потому что фактически означает: «Код написан с учетом параметризации Java, и здесь эта конструкция означает не то, что я использую низкоуровневый тип, а то, что параметр параметризации может содержать произвольный тип».
Второй пример демонстрирует важное практическое использование неограниченных метасимволов. Когда вы имеете дело с несколькими параметрами, иногда важно указать, что один параметр может относиться к произвольному типу, а другой ограничить определенным типом:
//: generics/UnboundedWi1dcards2.java
import java.util.*;
public class UnboundedWildcards2 { static Map mapl; static Map<?.?> map2; static Map<String,?> map3; static void assignlCMap map) { mapl = map; } static void assign2(Map<?,?> map) { map2 = map; } static void assign3(Map<String,?> map) { map3 = map; } public static void main(String[] args) { assignl(new HashMapO); assign2(new HashMapO); // assign3(new HashMapO); // Предупреждение: // Непроверенное преобразование. Обнаружен: HashMap // Требуется: Map<String,?> assignl(new HashMap<String,Integer>()); assign2(new HashMap<String,Integer>()); assign3(new HashMap<String.Integer>0):
}
} ///-
Когда в записи используются только неограниченные метасимволы, как в примере Мар<?,?>, компилятор не отличает такой тип от Map. Кроме того, пример UnboundedWildcardsl.java показывает, что компилятор по-разному интерпретирует List<?> и List<? extends Object>.
Ситуация осложняется тем, что компилятор не всегда интересуется различиями между List и List<?> (например), поэтому может показаться, что это одно и то же. В самом деле, поскольку параметризованный аргумент стирается до первого ограничения, List<?> кажется эквивалентным List<Object>, a List, по сути, тоже является List<Object> — однако ни одно из этих утверждений не является в полной мере истинным. List в действительности означает «низкоуровневый List, содержащий любой тип Object», тогда как List<?> означает «не-низкоуровне-вый List, содержащий какой-то конкретный тип, хотя мы не знаем, какой именно».
Когда же компилятор различает низкоуровневые типы и типы с неограниченными метасимволами? В следующем примере используется класс Holder<T>, определение которого приводилось ранее. Класс содержит методы, получающие аргумент Holder, но в разных формах: в виде низкоуровневого типа, с конкретным параметром типа, с неограниченным метасимволом:
//: generics/Wildcards.java // Exploring the meaning of wildcards.
public class Wildcards {
// Низкоуровневый аргумент: static void rawArgs(Holder holder. Object arg) { // holder set(arg); // Предупреждение-// Непроверенный вызов set(T) как члена // низкоуровневого типа Holder // holder, set (new WildcardsO); // To же предупреждение
// Невозможно: нет информации о 'Т' // T t = holder.getO.
// Допустимо, но информация типа теряется Object obj = holder getO.
}
// По аналогии с rawArgsO, но ошибки вместо предупреждений, static void unboundedArg(Holder<?> holder. Object arg) { // holder.set(arg); // Ошибка: // set(capture of ?) in Holder<capture of ?> // не может применяться к (Object) // holder, set (new WildcardsO). // Та же ошибка
// Невозможно; нет информации о 'Т': // T t = holder.get();
// Допустимо, но информация типа теряется: Object obj = holder.getO;
}
static <T> T exactl(Holder<T> holder) { T t = holder.getO; return t;
}
static <T> T exact2(Holder<T> holder. T arg) { holder.set(arg); T t = holder.getO; return t;
}
static <T>
T wildSubtype(Holder<? extends T> holder. T arg) { // holder.set(arg); // Ошибка: // set(capture of ? extends T) in // Holder<capture of ? extends T> // cannot be applied to (T) T t = holder.getO; return t;
}
static <T>
void wildSupertype(Holder<? super T> holder, T arg) { holder.set(arg);
// T t = holder.getO; // Ошибка:
// Несовместимые типы: обнаружен Object, требуется T
// Допустимо, но информация типа теряется: Object obj = holder get О;
}
public static void main(String[] args) {
Holder raw = new Holder<Long>(). // Или
raw = new Holder(),
Holder<Long> qualified = new Holder<Long>(), Holder<?> unbounded = new Holder<Long>(). Holder<? extends Long> bounded = new Holder<Long>(), Long Ing = 1L;
rawArgs(raw. Ing), rawArgs(qualified, Ing), rawArgs(unbounded. 1ng). rawArgs(bounded, Ing);
unboundedArg(raw, Ing), unboundedArg(qualified, Ing), unboundedArg(unbounded, Ing), unboundedArg(bounded, Ing),
// Object rl = exactl(raw); // Предупреждение // Непроверенное преобразование Holder в Holder<T> // Непроверенный вызов метода: exactlCHolder<T>) // применяется к (Holder) Long r2 = exactl(qualified),
Object r3 = exactl(unbounded), // Должен возвращать Object Long r4 = exactl(bounded),
// Long r5 = exact2(raw, Ing); // Предупреждения-
// Непроверенное преобразование Holder в Holder<Long>
// Непроверенный вызов метода. exact2(Holder<T>,T)
// применяется к (Holder,Long)
Long гб = exact2(qualified, Ing),
// Long r7 = exact2(unbounded. Ing), // Ошибка-
// exact2(Holder<T>,T) не может применяться к
// (Holder<capture of ?>,Long)
// Long r8 = exact2(bounded, Ing), // Ошибка.
// exact2(Holder<T>,T) не может применяться
// к (Holder<capture of ? extends Long>,Long)
// Long r9 = wildSubtype(raw, Ing); // Предупреждения
// Непроверенное преобразование Holder
// к Holder<? extends Long>
// Непроверенный вызов метода-
// wildSubtype(Holder<? extends T>,T)
// применяется к (Holder.Long)
Long rlO = wildSubtype(qualified. Ing);
// Допустимо, но возвращать может только Object-
Object rll = wildSubtype(unbounded. Ing).
Long rl2 = wildSubtype(bounded. Ing).
// wildSupertype(raw, Ing); // Предупреждения. // Непроверенное преобразование Holder // к Holder<? super Long> // Непроверенный вызов метода: // wildSupertype(Holder<? super T>,T) // применяется к (Holder.Long) wildSupertype(qualified, Ing), // wildSupertype(unbounded, Ing); // Ошибка:
// wildSupertype(Holder<? super T>,T) не может продолжение&
// применяться к (Holder<capture of ?>,Long) // wiIdSupertypeCbounded, Ing); // Ошибка: // wildSupertype(Holder<? super T>,T) не может // применяться к (Holder<capture of ? extends Long>.Long)
}
} ///:-
В методе rawArgs() компилятор знает, что Holder является параметризованным типом, поэтому несмотря на то, что здесь он выражен как низкоуровневый тип, компилятору известно, что передача Object методу set() небезопасна. Так как в данном случае используется низкоуровневый тип, методу set() можно передать объект произвольного типа, и он будет преобразован в Object. Таким образом, при использовании низкоуровневого типа вы лишаетесь проверки на стадии компиляции. Вызов get() демонстрирует ту же проблему: никакого Т нет, поэтому результатом может быть только Object.
Может создаться впечатление, что низкоуровневый Holder и Holder<?> — приблизительно одно и то же. Однако метод unboundedArgs() демонстрирует различия между ними — в нем выявляются те же проблемы, но информация о них выдается в виде ошибок, а не предупреждений, поскольку низкоуровневый Holder может содержать разнородные комбинации типов, тогда как Holder<?> содержит однородную коллекцию одного конкретного типа.
В exactl() и exact2() используются точные параметры типов (то есть без метасимволов). Мы видим, что exact2() обладает иными ограничениями, нежели exactl(), из-за дополнительного аргумента.
В wildSubtype() ограничения на тип Holder опускаются до Holder с элементами любого типа, удовлетворяющими условию extends Т. И снова это означает, что Т может быть типом Fruit, a holder сможет вполне законно стать Holder <Apple>. Чтобы предотвратить возможное размещение Orange в Holder<Apple>, вызовы set() (и любых других методов, получающих в аргументах параметр типа) запрещены. Однако мы знаем, что все объекты, полученные из Holder<? extends Fruit>, по меньшей мере, являются Fruit, поэтому вызов get() (или любого метода с возвращаемым значением параметра типа) допустим.
Реализация параметризованных интерфейсов
Класс не может реализовать две разновидности одного параметризованного интерфейса — вследствие стирания они будут считаться одним и тем же интерфейсом. Пример конфликта такого рода:
II: generics/MultiplelnterfaceVariants.java
II {CompileTimeError} (He компилируется)
interface Payable<T> {}
class Employee implements Payable<Employee> {}
class Hourly extends Employee
implements Payable<Hourly> {} ///:-
Класс Hourly компилироваться не будет, потому что стирание сокращает Payable<Employee> и Payable<Hourly> до Payable, а в приведенном примере это означало бы двукратную реализацию одного интерфейса. Интересная подробность: если удалить параметризованные аргументы из обоих упоминаний Payable, как это делает компилятор при стирании, программа откомпилируется.
Преобразования типов и предупреждения
Преобразование типа или instanceof с параметром типа не приводит ни к какому эффекту. В следующем контейнере данные хранятся во внутреннем представлении в форме Object и преобразуются к Т при выборке:
Подписывайтесь на наши страницы в социальных сетях.
Будьте в курсе последних книжных новинок, комментируйте, обсуждайте. Мы ждём Вас!
Похожие книги на "Философия Java3"
Книги похожие на "Философия Java3" читать онлайн или скачать бесплатно полные версии.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.
Отзывы о "Брюс Эккель - Философия Java3"
Отзывы читателей о книге "Философия Java3", комментарии и мнения людей о произведении.