Лиття змінних в Java


84

Цікаво, чи міг хтось сказати мені, як працює кастинг? Я розумію, коли мені це потрібно робити, але насправді не так, як це працює. У примітивних типах даних я розумію частково, але коли справа доходить до лиття об'єктів, я не розумію, як це працює.

Як можна об’єкт із типом Object просто раптово передати, скажімо, MyType(просто приклад), а потім отримати всі методи?


Рекомендоване читання: Спадщина
Франческо Менцані

Відповіді:


182

Кастинг на Java не є магією, це ви говорите компілятору, що об’єкт типу A насправді є більш конкретним типом B, і таким чином отримуєте доступ до всіх методів на B, яких у вас не було б інакше. Ви не виконуєте жодної магії чи конверсії під час виконання кастингу, ви по суті говорите компілятору "повірте мені, я знаю, що роблю, і можу гарантувати вам, що цей Об'єкт у цьому рядку насправді є <Вставити привід введіть тут>. " Наприклад:

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

Вище сказано добре, не магія, і все добре. Об'єкт, що зберігається в o, насправді є рядком, і тому ми можемо без проблем передати в рядок.

Існує два шляхи, коли це може піти не так. По-перше, якщо ви робите два типи в абсолютно різних ієрархіях успадкування, то компілятор знатиме, що ви дурний, і зупинить вас:

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

По-друге, якщо вони знаходяться в одній ієрархії, але все ще є недійсним приводом, тоді ClassCastExceptionбуде викинуто a під час виконання:

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

Це по суті означає, що ви порушили довіру компілятора. Ви сказали, що можете гарантувати, що об’єкт має певний тип, але це не так.

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

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


Дякую за пояснення. Якщо я правильно розумію це, мабуть, ні, коли ви кидаєте об'єкт, ви просто говорите компілятору, що я знаю, що об'єкт на цій адресі пам'яті знає, як реагувати на ці методи тощо, так що не відмовляйте мені? Це правильно?
user626912

1
@ user626912 Начебто - не думайте про це з точки зору адрес пам'яті. Ви не просто повідомляєте компілятору, що даний об’єкт відповідає інтерфейсу, і тому має реалізації заданих методів (ви можете створити абсолютно інший об’єкт з однаковими методами, і кастинг не обов’язково працюватиме.) Ви говорите компілятору, що об'єкт одного типу насправді є більш конкретним типом, і тому ви можете використовувати методи, доступні для цього більш конкретного об'єкта. Почитайте про поліморфізм, якщо ви цього ще не зробили, це може допомогти пояснити деякі речі.
Michael Berry

Я сумніваюся у вашому твердженні "воно вам потрібне лише при переході від більш загального типу до більш конкретного типу". ((Об'єкт) gpsLastLoc.getLatitude ()). GetClass (). GetSimpleName () поверне ім'я "подвійне" під час виконання, і це приклад використання кастингу з більш конкретного типу на більш загальний тип.
Фрукти

1
@ 林果 皞 Ви просто використовуєте кастинг у цьому випадку, щоб просувати примітив - це не те саме, що перейти до більш загального типу, і дуже дивний спосіб робити щось. Більш нормальний (кращий) спосіб був би Double.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName(). У будь-якому випадку вам ніколи не потрібно буде динамічно отримувати клас примітиву, оскільки, якщо getLatitude()повертається подвійний примітив, ви завжди знаєте, що він буде просуватися до Doubleоб’єкта.
Michael Berry

Мені подобається, як ви це висловлюєте, що код "порушив довіру компілятора". (Пс. У вас друкарська помилка, ви написали "ти" замість "ти".)
Джейсон Л.

7

Насправді кастинг не завжди працює. Якщо об'єкт не є instanceofкласом, до якого ви його передаєте, ви отримаєте його ClassCastExceptionпід час виконання.


5

Припустимо, ви хотіли привести а Stringдо File(так, це не має ніякого сенсу), ви не можете привести його безпосередньо, оскільки Fileклас не є дочірнім і не є батьківським Stringкласом (а компілятор скаржиться).

Але ви могли б віддати свій StringTo Object, тому що 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.

3

Правильний шлях такий:

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

Метод cast()є набагато безпечнішою альтернативою кастингу під час компіляції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.