Приведение переменных в Java

84

Интересно, может ли кто-нибудь сказать мне, как работает кастинг? Я понимаю, когда мне следует это сделать, но не совсем понимаю, как это работает. Я частично разбираюсь в примитивных типах данных, но когда дело доходит до приведения объектов, я не понимаю, как это работает.

Как можно внезапно привести объект с типом Object к, скажем, MyType(просто пример), а затем получить все методы?

user626912
источник
Рекомендуемая литература: Наследование
губка

Ответы:

182

Приведение в Java - это не волшебство, это вы сообщаете компилятору, что объект типа A на самом деле имеет более конкретный тип B, и таким образом получаете доступ ко всем методам на B, которых у вас не было бы иначе. Вы не выполняете никаких магических действий или преобразований при выполнении кастинга, вы, по сути, говорите компилятору: «поверьте мне, я знаю, что делаю, и я могу гарантировать вам, что этот объект в этой строке на самом деле является <Insert cast введите здесь>. " Например:

Object o = "str";
String str = (String)o;

Вышесказанное нормально, не волшебство и все хорошо. Объект, хранящийся в o, на самом деле является строкой, и поэтому мы можем без проблем преобразовать ее в строку.

Есть два пути, по которым это может пойти не так. Во-первых, если вы выполняете приведение между двумя типами в совершенно разных иерархиях наследования, компилятор поймет, что вы ведете себя глупо, и остановит вас:

String o = "str";
Integer str = (Integer)o; //Compilation fails here

Во-вторых, если они находятся в той же иерархии, но по-прежнему имеют недопустимое приведение, тогда во время выполнения ClassCastExceptionбудет брошено:

Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here

По сути, это означает, что вы нарушили доверие компилятора. Вы сказали, что можете гарантировать, что объект относится к определенному типу, но это не так.

Зачем нужен кастинг? Что ж, для начала вам это понадобится только при переходе от более общего типа к более конкретному. Например, Integerнаследуется от Number, поэтому, если вы хотите сохранить Integerкак a, Numberтогда это нормально (поскольку все целые числа являются числами). Однако, если вы хотите пойти другим путем, вам потребуется приведение - не все числа являются целыми числами (также как целое число мы имеем Double, Float, Byte, Longи т.д.) и даже если есть только один подкласс в вашем проекте или JDK, кто - то может легко создать еще и распространять это, так что вы не имеете никакой гарантии , даже если вы думаете , что это единственный, очевидный выбор !

Что касается использования приведения, вы все еще видите необходимость в нем в некоторых библиотеках. До Java-5 он активно использовался в коллекциях и различных других классах, поскольку все коллекции работали над добавлением объектов и последующим приведением результата, который вы получили обратно из коллекции. Однако с появлением дженериков большая часть использования приведения типов исчезла - они были заменены дженериками, которые обеспечивают гораздо более безопасную альтернативу, без потенциала для ClassCastExceptions (фактически, если вы используете дженерики чисто и он компилируется без предупреждений, у вас есть гарантия, что вы никогда не получите ClassCastException.)

Майкл Берри
источник
Спасибо за ваше объяснение. Если я понимаю это правильно, вероятно, нет, когда вы приводите объект, вы просто говорите компилятору, что я знаю, что объект на этом адресе памяти знает, как реагировать на эти методы и т.д. Это правильно?
user626912
1
@ user626912 Вроде - не думайте об адресах памяти. Вы не просто говорите компилятору, что данный объект соответствует интерфейсу и, следовательно, имеет реализации данных методов (вы можете создать совершенно другой объект с теми же методами, и приведение типов не обязательно будет работать). Вы говорите компилятору, что объект одного типа на самом деле является более конкретным типом, и поэтому вы можете использовать методы, доступные для этого более конкретного объекта. Прочтите о полиморфизме, если вы еще этого не сделали, это может помочь прояснить некоторые вещи.
Майкл Берри,
Я сомневаюсь в вашем утверждении, что «он вам нужен только при переходе от более общего типа к более конкретному». ((Object) gpsLastLoc.getLatitude ()). GetClass (). GetSimpleName () вернет "двойное" имя во время выполнения, и это пример использования приведения от более конкретного типа к более общему типу.
Fruit
1
@ 林果 皞 В этом случае вы просто используете приведение для продвижения примитива - это не то же самое, что переход к более общему типу, и очень странный способ делать что-то. Был бы более нормальный (лучший) способ Double.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName(). В любом случае вам никогда не нужно динамически получать класс примитива, поскольку, если getLatitude()возвращается двойной примитив, вы всегда знаете, что он будет повышаться до Doubleобъекта.
Майкл Берри,
Мне нравится, как вы это выразили, что код «нарушил доверие компилятора». (Ps. У вас есть опечатка, вы написали «вы» вместо «you».)
Джейсон Л.
7

На самом деле кастинг не всегда работает. Если объект не является instanceofклассом, к которому вы его приводите, вы получите ClassCastExceptionво время выполнения.

Sandrstar
источник
5

Предположим, вы хотите преобразовать a Stringв a File(да, это не имеет никакого смысла), вы не можете преобразовать его напрямую, потому что Fileкласс не является дочерним и не родительским для Stringкласса (и компилятор жалуется).

Но вы можете преобразовать свой Stringв Object, потому что a Stringявляется Object( Objectродительским). Затем вы можете преобразовать этот объект в объект File, потому что файл является файлом Object.

Таким образом, все ваши операции являются «законными» с точки зрения типизации во время компиляции, но это не означает, что они будут работать во время выполнения!

File f = (File)(Object) "Stupid cast";

Компилятор допустит это, даже если это не имеет смысла, но он выйдет из строя во время выполнения с этим исключением:

Exception in thread "main" java.lang.ClassCastException:
    java.lang.String cannot be cast to java.io.File
Кристоф Русси
источник
3

Приведение ссылки будет работать, только если она относится к instanceofэтому типу. Вы не можете использовать случайные ссылки. Кроме того, вам нужно больше узнать о Casting Objects.

например

String string = "String";

Object object = string; // Perfectly fine since String is an Object

String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.
asgs
источник
3

Правильный путь таков:

Integer i = Integer.class.cast(obj);

Этот метод cast()- гораздо более безопасная альтернатива приведению во время компиляции.

Егор256
источник