Явные и неявные приведения типов в Java
В Java существует много типов данных. В большинстве случаев при кодировании необходимо изменить тип данных, чтобы понять обработку переменной, и это называется приведением типа.
Что такое приведение типов в Java?
Приведение типов в Java – это присвоение значения одного примитивного типа данных другому. Вы должны знать о совместимости этого типа данных. Если они совместимы, тогда Java выполнит преобразование, известное как автоматическое преобразование типов, а если нет, то их необходимо явно преобразовать.
В Java есть два типа приведения:
- Расширение приведения (автоматически) – преобразование меньшего типа данных в больший размер типа. byte -> short -> char -> int -> long -> float -> double
- Сужение приведения (вручную) – преобразование данных большего размера в тип меньшего размера. double -> float -> long -> int -> char -> short -> byte
Неявное
Тип приведения, когда два типа данных автоматически конвертируются. Также известно как неявное преобразование. Происходит, когда два типа данных совместимы, а также когда мы присваиваем значение меньшего типа данных большему типу данных.
Например, числовые типы данных совместимы друг с другом, но автоматическое преобразование из числового типа в тип char или boolean не поддерживается.
Кроме того, char и boolean не совместимы друг с другом. Теперь давайте напишем логику для неявного приведения типов, чтобы понять, как это работает.
Явное
В этом случае, если вы хотите присвоить значение большего типа данных меньшему типу данных, вы можете выполнить явное приведение или сужение типов. Это полезно для несовместимых типов данных, где автоматическое преобразование невозможно.
Давайте разберемся с этим на примере.
Явное приведение в выражениях
Когда вы вычисляете выражения, выходные данные автоматически обновляются до большего типа данных операнда. Но если вы сохраните этот результат в каком-либо меньшем типе данных, он генерирует ошибку времени компиляции, из-за которой нам нужно набрать приведение вывода.
Примечание: в случае одиночных операндов результат конвертируется в int, а затем соответственно преобразуется в тип.
Преобразование с потерями в Java
В этом кратком руководстве мы обсудим концепцию преобразования с потерями в Java и причину этого.
В то же время мы рассмотрим некоторые удобные методы преобразования, чтобы избежать этой ошибки.
2. Преобразование С Потерями
Преобразование с потерями-это просто потеря информации при обработке данных.
В Java это соответствует возможности потери значения или точности переменной при преобразовании одного типа в другой.
Когда мы пытаемся назначить переменную типа большого размера типу меньшего размера , Java генерирует ошибку, несовместимые типы: возможное преобразование с потерями , при компиляции кода.
Например, давайте попробуем назначить long для int :
Java выдаст ошибку при компиляции этого кода:
Здесь Java найдет long и int несовместимыми и приведет к ошибке преобразования с потерями. Потому что могут быть длинные значения за пределами int диапазона от -2,147,483,648 до 2,147,483,647.
Аналогично, давайте попробуем назначить float a long :
Поскольку float может иметь десятичные значения, которые не имеют соответствующего long значения. Поэтому мы получим ту же ошибку.
Аналогично, присвоение double номера int приведет к той же ошибке:
Значения double могут быть слишком большими или слишком маленькими для int , и десятичные значения будут потеряны при преобразовании. Следовательно, это потенциальное преобразование с потерями.
Кроме того, мы можем столкнуться с этой ошибкой при выполнении простого вычисления:
Когда double умножается на int , мы получаем результат в double . Следовательно, это также потенциальное преобразование с потерями.
Поэтому несовместимые типы в преобразовании с потерями могут иметь разные размеры или типы (целые или десятичные числа).
3. Примитивные Типы данных
В Java существует множество примитивных типов данных, доступных с соответствующими классами-оболочками .
Далее давайте составим удобный список всех возможных преобразований с потерями в Java:
- short to byte или char
- char to байт или короткий
- int to byte , short или char
- long to byte , short , char или int
- float to byte , short , char , int или long
- double to byte , short , char , int , long или float
Обратите внимание, что хотя short и char имеют одинаковый размер. Тем не менее, преобразование из short в char приводит к потерям, поскольку char является типом данных без знака .
4. Методы преобразования
4.1. Преобразование Между Примитивными Типами
Простой способ преобразования примитивов, чтобы избежать преобразования с потерями,-это понижение; другими словами, приведение типа большего размера к типу меньшего размера. Следовательно, это также называется сужением примитивного преобразования.
Например, давайте преобразуем длинное число в короткое с помощью понижающей передачи :
Аналогично, давайте преобразуем double в int :
Однако следует отметить, что преобразование типа большого размера со слишком большими или слишком маленькими значениями в тип меньшего размера с помощью понижения может привести к неожиданным значениям.
Давайте преобразуем длинные значения за пределами диапазона короткие :
Если мы внимательно проанализируем конверсию, то увидим, что это не ожидаемые значения.
Другими словами, когда Java достигает наибольшего значения типа малого размера при преобразовании из типа большого размера, следующее число является наименьшим значением типа малого размера и наоборот.
Давайте разберемся в этом на примерах. Когда большое длинное число со значением 32768 преобразуется в короткое , значение короткое число 1 равно -32768 . Поскольку максимальное значение short равно 32767, поэтому Java переходит к следующему минимальному значению short.
Аналогично, когда маленькое длинное число преобразуется в короткое . Значение short Num2 равно 32767, поскольку Java переходит к следующему максимальному значению short .
Кроме того, давайте посмотрим, что произойдет, когда мы преобразуем максимальные и минимальные значения a long в int :
4.2. Преобразование между объектами-оболочками и примитивными типами
Чтобы напрямую преобразовать объект-оболочку в примитив, мы можем использовать различные методы в классах-оболочках, такие как int Value () , shortValue() и longValue() . Это называется распаковка .
Например, давайте преобразуем объект Float в объект long :
Кроме того, если мы посмотрим на реализацию long Value или аналогичных методов, мы найдем применение сужающего примитивного преобразования:
Однако иногда следует избегать сужения примитивного преобразования, чтобы сохранить ценную информацию:
После преобразования значение long Num будет равно 15. Однако двойное число равно 15.9999, что очень близко к 16.
Вместо этого мы можем использовать Math.round() для преобразования в ближайшее целое число:
4.3. Преобразование Между Объектами-Оболочками
Для этого давайте воспользуемся уже рассмотренными методами преобразования.
Сначала мы преобразуем объект-оболочку в примитивное значение, опустим его и преобразуем в другой объект-оболочку . Другими словами, мы будем выполнять техники распаковки, даункастинга и бокса.
Например, давайте преобразуем объект Double в объект Integer :
Наконец, мы используем Integer . valueOf() для преобразования примитивного типа int в Целое число объект. Этот тип преобразования называется boxing .
5. Заключение
В этой статье мы исследовали концепцию преобразования с потерями в Java с помощью ряда примеров. Кроме того, мы также составили удобный список всех возможных конверсий с потерями.
Попутно мы определили сужение примитивного преобразования как простой метод преобразования примитивных чисел и избежания ошибки преобразования с потерями.
В то же время мы также изучили дополнительные удобные методы числовых преобразований в Java.
Преобразование примитивных типов в Java. WDH: Java — Преобразования типов
Мы закончили нашу прошлую статью о на том, что я пообещал Вам рассказать, какие типы можно приводить и как это все делается. Давайте же приступим.
Приведение типов в арифметических выражениях выполняется автоматически.
Если операнды a и b комбинируются бинарным оператором (ниже мы это обсудим), перед его исполнением оба операнда преобразуются в данные одного типа следующим образом:
- Если один из операторов имеет тип double, второй также преобразуется в double;
- Если один из операторов имеет тип float, второй также преобразуется в float;
- Если один из операторов имеет тип long, второй также преобразуется в long;
- Если один из операторов имеет тип int, второй также преобразуется в int;

Разрешенные преобразования типов
Сплошные линии показывают преобразование, выполненное без потери информации. Это преобразование выполняется неявно. Преобразования, когда может произойти потеря информации, называются каст (casting). Они показанные штриховыми линиями. Если к типу данных на рисунке нет линий, то такое преобразование невозможно. Преобразования с потерей информации нужно проводить очень внимательно. Так, как можно потерять значительную часть данных и при этом программа может работать правильно.
Для сужения каст необходимо сделать явным. Например: byte b = (byte)128; прикастили инт к байт типу.
Предлагаю сделать несколько примеров.
Вы могли немного не понять данный код, так как я еще не объяснил, что такое компилятор, константы и т.д. Далее по обучению я все расскажу, хотя должен был сделать это раньше. А сейчас я хочу описать, какими правилами должны обладать названия переменных.
- Имена переменных не могут начинаться с цифры, в именах не могут использоваться как символы арифметических и логических операторов, а также символ ‘#’.
- Применение символов ‘$’ или ‘_’ приемлемо, включая первую позицию и имя.
- Переменная примитивного типа, объявленная как член класса (глобальная переменная), по умолчанию задается нулем.
- Если переменная объявлена как локальная переменная в методе, перед использованием она должна обязательно быть проинициализирована. Так как локальные переменные не изициализируются по умолчанию. Это значит, что Вы не можете объявить локальную переменную и оставить ее без инициализации. То есть вот так: int i;. Если Вы так сделаете в методе, компилятор попросит Вас задать значение по умолчанию в то время, как создав такую переменную как член класса (глобальную) компилятор сам задаст ей значение 0.
- Область действия и время жизни переменной ограничено блоком <>, в котором она объявлена. Если Вы создали переменную внутри метода (как мы это делали в примерах), то Вы не сможете использовать ее вне метода, так как метод ограничен скобками <>. Глобальную переменную видно во всех блоках.
- Также запрещено использовать зарезервированные слова java. Весь перечень ключевых слов можно увидеть на картинке ниже.

И так, как в этой статье я затронул выражение бинарный оператор, то предлагаю рассмотреть и операторы в Java. Тем более, что теории не так и много.
Java имеет несколько типов операторов: простое присваивание, арифметическое, унарное, равноправное и реляционное, условное, сравнение типов, побитовое и битовое смещение.
Много умных слов, но очень просто все объяснит вот эта картинка:

На первых порах мы будем пользоваться побитовым сравнением, присваиванием, и постфиксными операторами. Другие операторы встречаются не так часто, поэтому мы рассмотрим только те, которыми будем пользоваться.
public class OperatorsInJava <
int difference = a — b;
Java является строго типизированным языком программирования, а это означает, то что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции.
Виды приведений
В Java предусмотрено семь видом приведений:
- Тождественное (identity);
- Расширение примитивного типа (widening primitive);
- Сужение примитивного типа (narrowing primitive);
- Расширение объектного типа (widening reference);
- Сужение объектного типа (narrowing reference);
- Преобразование к строке (String);
- Запрещенные преобразования (forbidden);
byte b=3;
int a=b;
Следующие 19 преобразований являются расширяющими:
- От byte к short, int, long, float, double
- От short к int, long, float, double
- От char к int, long, float, double
- От int к long, float, double
- От long к float, double
- От float к double
long a = 111111111111L;
float f=a;
a=(long)f; // () это как раз и есть операция преобразования типа
System.out.println(a); //результат 111111110656
Обратите внимание – сужение – означает, что переход осуществляется от боле емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, т.е. программист в коде должен явно указать, то он намеревается осуществить такое преобразование и готов потерять данные.
Следующие 23 преобразования являются сужающими:
- От byte к char
- От short к byte, char
- От char к byte, short
- От int к byte, short, char
- От long к byte, short, char, int
- От float к byte, short, char, int, long
- От double к byte, short, char, int, long, float
System.out.println((byte)383);
System.out.println((byte)384);
System.out.println((byte)-384);
127
-128
-128
Видно, что знаковый бит при сужении не оказал никакого влияния, так как был просто отброшен – результат приведения обратных чисел (384, -384) оказался одинаковым. Следовательно, может быть потеряно не только точное абсолютное значение, но и знак величины.
Это верно и для char:
char c=4000;
System.out.println((short)c);
-25536
Преобразование ссылочных типов (расширение и сужение)
Преобразование объектных типов лучше всего иллюстрируется с помощью дерева наследования. Рассмотрим небольшой пример наследования:
class Parent <
int x;
>
class ChildY extends Parent <
int y;
>
class ChildZ extends Parent <
int z;
>
В каждом классе объявлено поле с уникальным именем. Будем рассматривать это поле как пример набора уникальных свойств, присущи некоторому объектному типу.
Объекты класса Parent обладают только одним полем x, а значит, только ссылки типа Parent могут ссылаться на такие объекты. Объекты класса ChildY обладают полем y и полем x, полученным по наследству от класса Parent. Стало быть, на такие объекты могут указывать ссылки типа ChildY или Parent. Пример:
Parent p = new ChildY();
Обратите внимание, что с помощью такой ссылки p можно обращаться лишь к полю x созданного объекта. Поле y недоступно, так как компилятор, проверяя корректность выражения p.y, не может предугадать, что ссылка p будет указывать на объект типа ChildY во время исполнения программы. Он анализирует лишь тип самой переменной, а она объявлена как Parent, но в этом классе нет поля y, что и вызовет ошибку компиляции.
Аналогично, объекты класса ChildZ обладают полем z и полем x, полученным по наследству от класса Parent. Значит, на такие объекты могут указывать ссылки типа ChildZ и Parent.
Таким образом, ссылки типа Parent могут указать на объект любого из трех рассматриваемых типов, а ссылки типа ChildY и ChildZ – только на объекты точно такого же типа. Теперь можно перейти к преобразования ссылочных типов на основе такого дерева наследования.
Расширение означает переход от более конкретного типа к менее конкретному, т.е. переход от детей к родителям. Подобно случаю с примитивными типами, этот переход производиться самой JVM при необходимости и «незаметен» для разработчика, то есть не требует никаких специальных преобразования.
Parent p1=new ChildY();
Parent p2=new ChildZ();
В обеих строках переменным типа Parent присваивается значение другого типа, а значит, происходит преобразование. Поскольку это расширение, оно производиться автоматически и всегда успешно.
Нужно заметить, что при подобном преобразовании с самим объектом ничего не происходит. Несмотря на то что, например, поле y класса ChildY теперь недоступно, это не значит, что оно исчезло. Такое существенно изменение объекта не возможно. Он был порожден от класса ChildY и сохраняет все его свойства. Изменился лишь тип ссылки, через которую идет обращение к объекту.
Обратный переход, то есть движение по дереву наследования вниз, к наследникам, является сужением. Например, для рассматриваемого случая, переход от ссылки типа Parent , которая может ссылаться на объекты трех классов, к ссылке типа ChildY, которая может ссылаться только на один класс из трех, очевидно, является сужением. Такой переход может оказаться невозможным. Если ссылка типа Parent ссылается на объект типа Parent или ChildZ, то переход к ChildY невозможен, так как в обоих случаях объект не обладает полем y, которое объявлено в классе ChildY. Поэтому при сужении разработчику необходимо явным образом указывать на то, что необходимо попытаться провести такое преобразование. JVM во время исполнения проверит корректность перехода. Если он возможен, преобразование будет проведено. Если же нет – возникнет ошибка (обычно ClassCastException).
Parent p=new ChildY();
ChildY cy = (ChildY)p; //верно
Parent p2=new ChildZ();
ChildY cy2 = (ChildY)p2; //ошибка
Чтобы проверить, возможен ли желаемый переход, можно воспользоваться оператором instanceof:
Parent p=new ChildY();
if (p instanceof ChildY) <
ChildY cy = (ChildY)p;
>
Parent p2=new ChildZ();
if (p2 instanceof ChildY) <
ChildY cy = (ChildY)p2;
>
Parent p3=new Parent();
if (p3 instanceof ChildY) <
ChildY cy = (ChildY)p3;
>
В данном примере ошибок не возникнет. Первое преобразование возможно, и оно будет осуществлено. Во втором и третьем случаях условия операторов if не сработают и следовательно некорректного перехода не будет.
Преобразование к строке
Любой тип может быть приведен к строке, т.е. к экземпляру класса String. Такое преобразование является исключительным в силу того, что охватывает абсолютно все типы.
Различные типы преобразуются к строке следующим образом:
- Числовые типы записываются в текстовом виде без потери точности представления. Сначала на основе примитивного значения порождается экземпляр соответствующего класса-«обертки», затем у него вызывается метод toString(). Но поскольку эти действия снаружи незаметны, JVM оптимизирует их и преобразует примитивные значения в текст напрямую.
- Булевские величины приводятся к строке «true» или «false» в зависимости от значения.
- Для объектных величин вызывается метод toString(). Если метод возвращает null, то результатом будет строка “null”.
- Для null-значения генерируется строка “null”.
Применение приведений
Ситуации применения преобразования типов могут быть сгруппированы следующим образом:
- Присвоение значений переменным (assignment). Не все переходы допустимы при таком преобразовании – ограничения выбраны таким образом, чтобы не могла возникнуть исключительная ситуация.
- Вызов метода. Это преобразование применяется к аргументам вызываемого метода или конструктора. Такое приведение никогда не порождает ошибок. Так же приведение осуществляется при возвращении значения метода.
- Явное приведение. В этом случае явно указывается, к какому типу требуется привести исходное значение.
- Оператор конкатенации производит преобразование к строке своих аргументов.
- Числовое расширение. Числовые операции могут потребовать изменения типа аргумента(ов). Это преобразование имеет особое название – расширенное, так как выбор целевого типа может зависеть не только от исходного значения, но и от второго аргумента операции.
В данном уроке мы расскажем вам о небольшой головной боли программистов — приведении типов. Что такое приведение типов? Это любое преобразование типа данных.
Например:
Int b = 3;
double a = 1.0 * b;//преобразование типов
a = (double)b;//преобразование типов
Таким образом можно увидеть два способа изменения типа:
- Выполнение каких-то операций над объектом
- Явное преобразование
К каким типам можно приводить? Можно приводить к таким типам данных, которые находятся в одной иерархии. Допустим можно привести целое число к вещественному и наоборот. Можно привести класс Student к классу User и так далее. Очевидно, что приводить строку к числу бесполезно, так как это разные объекты. В таком случае обычно пользуются специальными операциями.
У более менее опытных пользователей может возникнуть следующий вопрос:
Int b = 3;
double a = b;//преобразование типов 1
b = (int) a;//преобразование типов 2
Почему типу данных double можно присваивать тип данных int и компилятор не выдаст ошибку, а для того, чтобы double привести в int нужно явно указать тип? Оказывается безопасные преобразования, например от int к double или от сына к родителю называют расширяющими, т.е мы даем типу данных с более низкими возможностями расширится, например целому типу данных, даем возможность становится вещественным, расширяя его область применения. Преобразование называется расширяющим, если тип данных к которому мы приводим включает в себя тип данных который мы хотим привести для базовых типов.
Сужающие преобразования всегда связаны с некоторой потерей информации, например преобразовывая от double к int мы теряем все значения после запятой, что вызывает опасения у компьютера и только явное указание типа данных может уверить его, что мы делаем это преобразование в здравом уме и твердой памяти.
Рассмотрим еще раз пример с фигурами:
Public class Shape <
>
public class Square extends Shape <
>
Square square;
Shape shape = square;//расширяющие преобразование
square = shape;//сужающие преобразование
Казалось бы преобразовывая от сына к родителю мы наоборот сужаемся, а не расширяемся, в чем причина? А причина состоит в том, что на самом деле класс Square содержит всю информацию класса Shape и преобразовываясь от сына к отцу, мы только теряем информацию специфичную для сына, которая в данный момент может быть не важна, но преобразовываясь от Shape к Square мы можем получить такую ситуацию, что у нас просто нет данных, необходимой для работы класса, например у нас нет размера квадрата, если говорить о примере выше.
И в завершении урока рассмотрим оператор instanceof, он возвращает true если объект имеет заданный тип:
If(new Square() instanceof Shape)/false
1. Что такое явное и автоматическое приведение типов в выражениях?
Приведение типов может быть явное и автоматическое.
При явном приведении типов сама операция приведения задается явным образом.
При автоматическом приведении типов нужно, чтобы выполнялись два условия:
- оба типа должны быть совместимыми;
- длина исходного типа (типа источника) должна быть меньше длины целевого типа (типа приемника).
2. Как выглядит явное приведение типов в выражениях? Примеры
Явное приведение типов позволяет осуществлять присвоение несовместимых типов. Общая форма явного приведения типов имеет вид:
(целевой_тип) значение
целевой_тип – это тип, в который нужно привести указанное значение .
Примеры явного приведения типов.
// явное приведение типов в выражениях byte b; int a; double d; float f; d = -39.9203; a = (int )d; // a = -39 f = (float )d; // f = -39.9203 b = (byte )d; // b = -39 d = 302930932; b = (byte )d; // b = -12 — урезание значения a = -27; b = (byte )a; // b = -27
3. Примеры автоматического приведения типов
Пример 1 . Автоматическое приведение целочисленных типов.
// автоматическое приведение целочисленных типов int a; byte b; short sh; b = -23; a = b; // a = -23 — автоматическое приведение типов sh = -150; a = sh; // a = -150 long l = 200; // Ошибка: «Type mismatch: cannot convert from long to int» // a = l; l = b; // l = -23 l = sh; // l = -150 char c = «Z» ; a = c; // a = 90 — код символа «Z» boolean b1 = false ; //a = b1; — ошибка, типы несовместимые
Пример 2 . Автоматическое приведение типов с плавающей запятой.
// автоматическое приведение типов с плавающей запятой float f; double d; f = 3.409033f; d = f; // d = 3.409033
Пример 3 . Автоматическое приведение смешанных типов. Такой случай возможен, если переменной типа с плавающей запятой присваивается значение переменной целочисленного типа.
// автоматическое приведение смешанных типов float f; double d; a = 28; d = a; // d = 28.0 f = a; // f = 28.0 // Ошибка: Type mismatch: cannot convert from float to int // a = f;
4. Как осуществляется автоматическое продвижение типов в выражениях?
Автоматическое продвижение типов происходит в выражениях. При этом, значение, которое фигурируют в выражениях, автоматически продвигаются к типам с большими диапазонами значений.
При автоматическом продвижении типов в выражениях:
- если один из целочисленных операндов имеет тип int, то все значения типов byte, short и char продвигаются к типу int;
- если один из целочисленных операндов имеет тип long, то все выражение продвигается к типу long;
- если один из операндов относится к типу float, то тип всего выражения будет также типа float (если нет операндов типа double);
- если один из операндов относится к типу double, то тип всего выражения будет также double.
5. Пример продвижения из типа byte в int в котором выражение не содержит операндов-переменных типа int (long)
Вышеприведенный пример работает корректно, так как:
- результат помещается (совместим) в тип byte;
- нет операндов типа int.
В вышеприведенном примере значения 1000 превышает диапазон значений типа byte. Сначала число 1000 приводится к типу int. Но результат
1000 / 20 = 50
приводится к типу byte и может корректно поместиться в переменной b .
Если написать так:
byte b; b = 100000 / 20; // ошибка, так как результат не помещается в тип byte
то выйдет ошибка компиляции с выводом сообщения:
В этом случае результат не помещается в тип byte:
100000 / 20 = 5000
Тогда это число (5000) автоматически становится типом int и компилятор выдаст сообщение об ошибке.
Если сделать явное приведение типов:
byte b; b = (byte ) (100000 / 20); // b = -120
то в этом случае результат 5000 типа int превращается в тип byte. Как известно, переменная типа int занимает 32 бита, а переменная типа byte занимает 8 бит. Значение переменной типа int урезается. И имеем то, что имеем (b = -120 ).
Вышеприведенные примеры относятся и к переменным типов short и char.
6. Пример. Продвижение из типа byte в тип int, в котором выражение содержит операнд-переменную типа int
В вышеприведенном примере в выражении используется переменная d типа int . Поэтому компилятор выдаст сообщение об ошибке:
Type mismatch: cannot convert from int to byte
Это означает, что результат есть типа int (а не byte ) даже если значение помещается в диапазон значений типа byte. Поскольку в выражении используется переменная-операнд d типа int.
Если осуществить явное приведение типов, то результат будет корректным:
// продвижение типов в выражениях // byte -> int byte b; int d; d = 20; b = (byte )(1000 / d); // b = 50 — работает корректно
7. Пример. Продвижение из типа int в тип long
Пример продвижения типов из int в long. Если один из операндов есть типа long, то все выражение продвигается к типу long.
int d; long l; d = 10000 * 200; // работает, d = 2000000 // Ошибка! Type mismatch: cannot convert from long to int // d = 1L * 2L; — операнды 1L и 2L есть типа long l = 100; // ошибка, один из операндов есть типа long // d = l * 2;
Как видно из примера, если один из операндов есть типа long , то все выражение становится типа long .
Почему меня не слышит абонент
Xiaomi redmi note 2 16gb прошивка
Не приходят уведомления на устройства xiaomi
Как отключить уведомления для любого приложения на Android Режим предотвращения набора номера отключить
Объектно-ориентированное программирование
127
-128
-128
Видно, что знаковый бит при сужении не оказал никакого влияния, так как был просто отброшен – результат приведения обратных чисел (384, -384) оказался одинаковым. Следовательно, может быть потеряно не только точное абсолютное значение, но и знак величины.
Это верно и для char:
-25536
Преобразование ссылочных типов (расширение и сужение)
Преобразование объектных типов лучше всего иллюстрируется с помощью дерева наследования. Рассмотрим небольшой пример наследования:
В каждом классе объявлено поле с уникальным именем. Будем рассматривать это поле как пример набора уникальных свойств, присущи некоторому объектному типу.
Объекты класса Parent обладают только одним полем x, а значит, только ссылки типа Parent могут ссылаться на такие объекты. Объекты класса ChildY обладают полем y и полем x, полученным по наследству от класса Parent. Стало быть, на такие объекты могут указывать ссылки типа ChildY или Parent. Пример:
Parent p = new ChildY();
Обратите внимание, что с помощью такой ссылки p можно обращаться лишь к полю x созданного объекта. Поле y недоступно, так как компилятор, проверяя корректность выражения p.y, не может предугадать, что ссылка p будет указывать на объект типа ChildY во время исполнения программы. Он анализирует лишь тип самой переменной, а она объявлена как Parent, но в этом классе нет поля y, что и вызовет ошибку компиляции.
Аналогично, объекты класса ChildZ обладают полем z и полем x, полученным по наследству от класса Parent. Значит, на такие объекты могут указывать ссылки типа ChildZ и Parent.
Таким образом, ссылки типа Parent могут указать на объект любого из трех рассматриваемых типов, а ссылки типа ChildY и ChildZ – только на объекты точно такого же типа. Теперь можно перейти к преобразования ссылочных типов на основе такого дерева наследования.
Расширение означает переход от более конкретного типа к менее конкретному, т.е. переход от детей к родителям. Подобно случаю с примитивными типами, этот переход производиться самой JVM при необходимости и «незаметен» для разработчика, то есть не требует никаких специальных преобразования.
Parent p1=new ChildY();
Parent p2=new ChildZ();
В обеих строках переменным типа Parent присваивается значение другого типа, а значит, происходит преобразование. Поскольку это расширение, оно производиться автоматически и всегда успешно.
Нужно заметить, что при подобном преобразовании с самим объектом ничего не происходит. Несмотря на то что, например, поле y класса ChildY теперь недоступно, это не значит, что оно исчезло. Такое существенно изменение объекта не возможно. Он был порожден от класса ChildY и сохраняет все его свойства. Изменился лишь тип ссылки, через которую идет обращение к объекту.
Обратный переход, то есть движение по дереву наследования вниз, к наследникам, является сужением. Например, для рассматриваемого случая, переход от ссылки типа Parent , которая может ссылаться на объекты трех классов, к ссылке типа ChildY, которая может ссылаться только на один класс из трех, очевидно, является сужением. Такой переход может оказаться невозможным. Если ссылка типа Parent ссылается на объект типа Parent или ChildZ, то переход к ChildY невозможен, так как в обоих случаях объект не обладает полем y, которое объявлено в классе ChildY. Поэтому при сужении разработчику необходимо явным образом указывать на то, что необходимо попытаться провести такое преобразование. JVM во время исполнения проверит корректность перехода. Если он возможен, преобразование будет проведено. Если же нет – возникнет ошибка (обычно ClassCastException).
Parent p=new ChildY();
ChildY cy = (ChildY)p; //верно
Parent p2=new ChildZ();
ChildY cy2 = (ChildY)p2; //ошибка
Чтобы проверить, возможен ли желаемый переход, можно воспользоваться оператором instanceof:
Parent p=new ChildY();
if (p instanceof ChildY) <
ChildY cy = (ChildY)p;
>
Parent p2=new ChildZ();
if (p2 instanceof ChildY) <
ChildY cy = (ChildY)p2;
>
Parent p3=new Parent();
if (p3 instanceof ChildY) <
ChildY cy = (ChildY)p3;
>
- Числовые типы записываются в текстовом виде без потери точности представления. Сначала на основе примитивного значения порождается экземпляр соответствующего класса-«обертки», затем у него вызывается метод toString(). Но поскольку эти действия снаружи незаметны, JVM оптимизирует их и преобразует примитивные значения в текст напрямую.
- Булевские величины приводятся к строке «true» или «false» в зависимости от значения.
- Для объектных величин вызывается метод toString(). Если метод возвращает null, то результатом будет строка “null”.
- Для null-значения генерируется строка “null”.
- Присвоение значений переменным (assignment). Не все переходы допустимы при таком преобразовании – ограничения выбраны таким образом, чтобы не могла возникнуть исключительная ситуация.
- Вызов метода. Это преобразование применяется к аргументам вызываемого метода или конструктора. Такое приведение никогда не порождает ошибок. Так же приведение осуществляется при возвращении значения метода.
- Явное приведение. В этом случае явно указывается, к какому типу требуется привести исходное значение.
- Оператор конкатенации производит преобразование к строке своих аргументов.
- Числовое расширение. Числовые операции могут потребовать изменения типа аргумента(ов). Это преобразование имеет особое название – расширенное, так как выбор целевого типа может зависеть не только от исходного значения, но и от второго аргумента операции.
posted by Sergey A Brazhnik at 09:38
2 Comments:
Это связано с тем, что char, в отличие от остальных целочисленных типов, является знаковым.
Может быть наоборот? Или тогда по-подробнее объясните пожалуйста.
Это приведение значений int к типу float и приведение значений типа long к типу float или double. Хотя эти дробные типы вмещают гораздо большие числа, чем соответствующие целые, но у них меньше значащих разрядов.
Непонятно, а пример приводится на сужение на самом деле
long a = 111111111111L;
float f=a;
a=(long)f; // () это как раз и есть операция преобразования типа
System.out.println(a); //результат 111111110656
Не понятно то что под заголовком «Применение приведений», особенно
числовое расширение и вызов метода. Нельзя ли по-подробнее, что имелось ввиду.
Заранее благодарю.
About
Блог учебных материалов по семинарам спец. курса ММФ НГУ «Объектно-ориентированное программирование» (автор Замулин А.В.).