Предположим, у меня есть три класса A, B и C.
C расширяет B, а B расширяет A. A реализует интерфейс InterfaceA B реализует интерфейсы InterfaceB1 и InterfaceB2.
Как лучше всего найти все возможные комбинации понижения?
Я имею в виду?
Предположим, у нас есть: Бб = новый Б(); Интерфейс A i = (Интерфейс A)(A)(B) b.
Как мы можем легко узнать, компилируется ли это и вызовет ли это classcastException без IDE?
Я знаю, как работают объекты и ссылки, и неплохо разбираюсь в полиморфизме в Java.
Я начал рисовать набросок структуры класса и интерфейса.
Обновлено: я знаю, что мой пример неверен, но я всегда борюсь, когда интерфейсы присоединяются к истории.
«Как мы можем легко узнать, скомпилируется ли это», попробуйте скомпилировать его.
Я это тоже знаю, я задаю этот вопрос, потому что я готовлюсь к экзамену с такими вопросами, а у нас есть только бумага и ручка.
Мое эмпирическое правило: очень хорошо подумайте, прежде чем бросать один раз. Никогда не произносите заклинание дважды, потому что оно приносит только боль и страдание.
Предполагая, что классы имеют осмысленные и логичные имена, это не должно быть так сложно.




Давайте сначала разберемся с тем, компилируется он или нет.
Ключом к определению этого является просмотр типа времени компиляции, то есть объявленного типа переменных.
В целом,
Чтобы определить, успешно ли выполнено приведение во время выполнения, необходимо посмотреть на тип времени выполнения объекта, хранящегося в переменной. Например:
A a = new B();
Тип времени компиляции a — A, а тип времени выполнения — B.
Как правило, приведение типа класса среды выполнения T к типу класса или интерфейса U выполняется успешно только в том случае, если T наследуется от U или реализует U.
Для будущих читателей,
Если вместо этого вы хотите узнать, как проверить успешность приведения используя код, вам следует обратиться к этому вопрос.
ваше объяснение уже довольно приятно. однако для полноты, я думаю, вы можете добавить практическую сторону вещей, т.е. "foo.getClass().isAssignableFrom(Bar.class)" и "foo instanceof Bar"
@rmalchow Это уже покрыто другим вопросом. ОП здесь только хочет знать, как это сделать с ручкой и бумагой.
ты редактировал это? да, я думаю, ссылка на другой ответ в порядке.
Спасибо за ваше объяснение. Если я правильно понял, InterfaceB2 b2 = (InterfaceB2) a;, это то же самое, что InterfaceB2 b2 = (InterfaceB2) (B) (A) a;?
@AyoubRossi Если под a вы имеете в виду a в A a = new B();, то да, эти два оператора делают одно и то же, преобразуя A в InterfaceB2. Другими словами, промежуточные приведения к B и A избыточны.
Спасибо, последний вопрос. Предположим, у нас есть класс B, реализующий интерфейс Ib, и B b = new B(); Ib interface = b не будет работать. Должно быть Ib interface = (Ib) b. В чем причина этого?
Ib interface = b; не будет работать нет, потому что вам нужно приведение, а потому что interface является зарезервированным словом и поэтому не может быть именем переменной. Ib ib = b; должен компилироваться. @АюбРосси
Существуют ли случаи, когда обязательно писать два приведения или требуется только первое, а остальные избыточны?
@AyoubRossi Почти всегда вам нужен только один актерский состав. Я сказал «почти», потому что могут быть действительно очень неясные случаи, о которых я не знаю. Я лично не сталкивался с такими случаями. Также обратите внимание, что иногда добавление большего количества приведений вызовет исключение. Предположим, что b имеет тип среды выполнения B, (InterfaceB1)b завершится успешно, но (InterfaceB1)(C)b завершится с ошибкой с ClassCastException, потому что он сначала попытается преобразовать b в C.
Прежде всего, в вашем сценарии следующее вовсе не является понижением, потому что в описанной иерархии B - это B, B - это A, а B - это InterfaceA. Таким образом, любое из этих приведений может быть опущено.
InterfaceA i = (InterfaceA)(A)(B) b;
Затем, отвечая на ваш вопрос. Понижающее приведение будет компилироваться тогда и только тогда, когда возможно добиться успеха во время выполнения. Введем дополнительный класс D, расширяющий класс A и реализующий InterfaceB1.
A a = new A();
InterfaceB1 b1 = (InterfaceB1) a; // compilable as A reference can contain object of class B that implements InterfaceB1
InterfaceB2 b2 = (InterfaceB2) a; // compilable as A reference can contain object of class B that implements InterfaceB2
b1 = (InterfaceB1) b2; // compilable as InterfaceB2 reference can contain object of class B that implements both InterfaceB1, InterfaceB2
B b = (B) a; // compilable as A reference can contain object of class B
C c = (C) a; // compilable as A reference can contain object of class C
D d = (D) a; // compilable as A reference can contain object of class D
d = (D) b1; // compilable as InterfaceB1 reference can contain object of class D in runtime
b = (B) d; // not compilable since D reference can NEVER contain object of class B in runtime
c = (C) d; // not compilable since D reference can NEVER contain object of class D in runtime
Что касается возникновения ClassCastException, это всегда связано с тем, что на самом деле содержится в вашей ссылке на объект.
A a1 = new A();
A a2 = new B();
InterfaceB1 b1 = (InterfaceB1) a1; // compiles but causes ClassCastException as A cannot be cast to InterfaceB1
InterfaceB1 b2 = (InterfaceB1) a2; // compiles and runs just normally as B can be cast to InterfaceB1
Как мне подойти к этой проблеме, не компилируя ее?