Ответ 1
Я считаю, что мы можем уменьшить ваш вопрос до: Почему следующий пример не удается скомпилировать?
public class Foo {
private final String bar = "bar";
public <T extends Foo> void printFoo(T baz) {
System.out.println(baz.bar); //bar is not visible
}
}
Это отличный вопрос, и это наверняка застало меня врасплох. Но мы можем фактически удалить Generics из уравнения, заметив, что это тоже не работает:
public class Foo {
private final String bar = "bar";
public void printFoo(SubFoo baz) {
System.out.println(baz.bar); //bar is not visible
}
}
class SubFoo extends Foo {
}
Другими словами, проблема в том, что вы имеете дело с подклассом Foo
, а не Foo
. В случае T
мы не знаем, какой подкласс, но мы знаем, что это подкласс или Foo
.
Как вы уже поняли, решение (неожиданно, по крайней мере, для меня) - это upcast:
System.out.println(((Foo)baz).bar);
Или для общего случая:
public <T extends Foo> void printFoo(T baz) {
System.out.println(((Foo)baz).bar);
}
Является ли бросок настолько плохим? На самом деле, нет. Это конечно же хорошо или лучше, чем избегать приведения с промежуточной переменной. Как и при любом повышении, я предполагаю, что он будет удален компилятором. Он существует только как подсказка для компилятора. Мы, конечно, не должны беспокоиться о безопасности приведения, потому что стирание T
уже Foo
.
Я могу только предположить, что это ограничение требуется для того, чтобы быть понятным о доступе... поскольку SubFoo
может обновлять bar
сам, он может стать двусмысленным, к которому относится bar
, и поэтому листинг необходимо. Это продемонстрировано в этом сложном примере:
public class Foo {
private final String bar = "hello";
static class SubFoo extends Foo {
private final String bar = "world";
}
public <T extends SubFoo> void printFoo(T baz) {
// System.out.println(baz.bar); // doesn't compile
System.out.println(((Foo)baz).bar); //hello
System.out.println(((SubFoo)baz).bar); //world
}
public static void main(String[] args) {
new Foo().printFoo(new SubFoo()); //prints "hello\nworld"
}
}
В этом отношении он служит скорее как квалификатор, чем как литой.