Тип приведения не меняет тип объекта.Это только изменяет тип времени компиляции ссылки на него, с проверкой во время выполнения относительно действительности, когда это необходимо.
Так как приведение не меняет тип объекта, оно никогда не изменяет результат вызовапереопределяемого метода, такого как ваш getX()
, который всегда будет вызывать самый конкретный метод.Аналогично, при добавлении объекта к String
приведение не имеет никакого эффекта, так как результатом будет вызов переопределяемого метода toString()
.
Когда дело доходит до полей или private
методов,тип ссылки во время компиляции может влиять на доступ к полю или методу, однако, поскольку знания о типе ограничены для параметра типа универсального метода, вы не можете ожидать воздействия только для типа.вызывающий абонент знает.
Поэтому, когда вы замените
@SuppressWarnings("unchecked")
public <T extends CL0> String getClazzPath(Class<T> clazz) {
System.out.println("clazz : " + clazz);
System.out.println("cast : " + clazz.cast(this));
System.out.println("cast.x : " + clazz.cast(this).x);
System.out.println("cast.getX() : " + clazz.cast(this).getX());
System.out.println("#");
return clazz.cast(this).x + (clazz == CL0.class ? "" : "/" + getClazzPath((Class<T>) clazz.getSuperclass()));
}
на
@SuppressWarnings("unchecked")
public <T extends CL0> String getClazzPath(Class<T> clazz) {
System.out.println("clazz : " + clazz);
System.out.println("cast : " + (T)this);
System.out.println("cast.x : " + ((T)this).x);
System.out.println("cast.getX() : " + ((T)this).getX());
System.out.println("#");
return clazz.cast(this).x + (clazz == CL0.class ? "" : "/" + getClazzPath((Class<T>) clazz.getSuperclass()));
}
, результат не изменится.Нет разницы между обычным типом приведений (T)…
и clazz.cast(…)
, когда clazz
является Class<T>
, оба имеют эффект изменения типа времени компиляции ссылки на T
¹ .
Но что такое T
?Метод не знает, все, что он знает, это то, что он может быть назначен на CL0
, благодаря объявлению <T extends CL0>
, и, следовательно, позволяет получить доступ к членам CL0
, включая поле CL0.x
.
Вы можете считать ошибкой проектирования языка, чтобы разрешить доступ к не перезаписываемым элементам, таким как поля CL0
, через ссылку типа T
, хотя T
может быть подклассом, объявляющим свое собственное поле с тем же именем,Фактически, для private
членов компилятор генерирует ошибку при обращении к ним по ссылке типа T
, даже когда private
члены CL0
доступны.
Комудалее продемонстрируйте, что нет разницы между приведением обычного типа и Clazz.cast
, вы можете изменить
System.out.println("cl3.getX()=" + cl3.getX());
System.out.println("((CL2)cl3).getX()=" + ((CL2) cl3).getX());
System.out.println("((CL1)cl3).getX()=" + ((CL1) cl3).getX());
System.out.println("((CL0)cl3).getX()=" + ((CL0) cl3).getX());
System.out.println("((IdentyfiedScope)cl3).getX()=" + ((Base) cl3).getX());
System.out.println();
System.out.println("cl3.x=" + cl3.x);
System.out.println("((CL2)cl3).x=" + ((CL2) cl3).x);
System.out.println("((CL1)cl3).x=" + ((CL1) cl3).x);
System.out.println("((CL0)cl3).x=" + ((CL0) cl3).x);
System.out.println("((IdentyfiedScope)cl3).x=" + ((Base) cl3).x);
вашего main
метода на
System.out.println("cl3.getX()=" + cl3.getX());
System.out.println("((CL2)cl3).getX()=" + CL2.class.cast(cl3).getX());
System.out.println("((CL1)cl3).getX()=" + CL1.class.cast(cl3).getX());
System.out.println("((CL0)cl3).getX()=" + CL0.class.cast(cl3).getX());
System.out.println("((IdentyfiedScope)cl3).getX()=" + Base.class.cast(cl3).getX());
System.out.println();
System.out.println("cl3.x=" + cl3.x);
System.out.println("((CL2)cl3).x=" + CL2.class.cast(cl3).x);
System.out.println("((CL1)cl3).x=" + CL1.class.cast(cl3).x);
System.out.println("((CL0)cl3).x=" + CL0.class.cast(cl3).x);
System.out.println("((IdentyfiedScope)cl3).x=" + Base.class.cast(cl3).x);
, и результат также будеттот же самый.Нет никакой разницы между этими подходами, все, что имеет значение, это то, что в одном месте вы применяете конкретные типы CL3
, CL2
, CL1
, CL0
или Base
, в другом,вы приводите к параметру типа T
.
¹ Есть разница, что Class.cast
будет проверять действительность во время выполнения, в отличие от непроверенного приведения, но так каквсе они действительны в этом примере, результат не меняется, и мы можем сосредоточиться на влиянии на выбор участника.