Bom dia, fazendo o TestKiller me deparei com essa questão:
class Foo {
public int a;
public Foo() { a = 3; }
public void addFive() { a += 5; }
}
public class Bar extends Foo {
public int a;
public Bar() { a = 8; }
public void addFive() { this.a +=5; }
public static void main( String[] args ) {
Foo foo = new Bar();
foo.addFive();
System.out.println("Value: " + foo.a);
}
}
e as alternativas:
A. Value: 3
B. Value: 8
C. Value: 13
D. Compilation fails.
E. The code runs with no output.
F. An exception is thrown at runtime.
A resposta correta é a letra a, Value: 3
Posso estar sendo cego e ser óbvio o pq disso, mas não estou entendendo, será que alguem poderia me explicar essa questão?
Mas mesmo se a gente tirar o this ele vai usar o atributo de Bar… só se a gente colocasse o super.a que ele chamaria o da super classe ali… só me responde… isso é sombreamento??
Eu não consegui compreender ainda o porque desta resposta. Quando chegar em casa a noite vou recorrer ao livro da Kathy para ver se encontro algo que justitique esta resposta e também vou analisar esta questão novamente.
Mas, por enquanto, ainda continuo achando que seria 13.
Alguém que compreendeu bem, poderia fazer um passo-a-passo aí da execução do programa, com explicação 8) .
Senão depois faço um debug.
class Foo {
public int a;
public Foo() { a = 3; }
public void addFive() { a += 5; }
}
public class Bar extends Foo {
public int a;
public Bar() { a = 8; }
public void addFive() { this.a +=5; }
public static void main( String[] args ) {
Foo foo = new Bar();
foo.addFive();
System.out.println("Value: " + foo.a);
}
}
Na chamada a Foo foo = new Bar(); ele chama o construtor de Bar que chama o de Foo… nisso temos duas variáveis de instância Foo.a e Bar.a
Foo.a = 3 e Bar.a = 8
então ele chama foo.addFive() e é chamado o método de Bar que está devidamente sobrescrito, o que deixa Bar.a = 13 como você mesmo disse…
E então temos a chamada a foo.a que chama a variável de Foo que é o tipo da instância Foo foo, cujo valor é foo.a = 3 já que a variável a de Foo foi apenas inicializada com o valor 3… depois não fizemos outras alterações nela, apenas na variável de Bar;
Os atributos não são subscritos, no caso do atributo “a”.
Qdo foi inicializado o Bar automaticamente foi feita a chamada do construtor super que é o Foo e então foi atribuido o valor 3 para o atributo “a”. E no System.out.println("Value: " + foo.a); ele vai chamar o atributo correspondente ao Foo e nao ao Bar.
Cara a regra é a seguinte: A herança existe apenas para metodos desde que nao sejam estaticos, ou seja, voce nao pode sobrescrever uma variavel de instantia. Quando voce declarou int a ; na classe Foo e int a; na classe Bar, cada uma é independente de si. Quando vc intanciou, da seguinte forma :
Foo foo = new Bar(); voce esta utilizando uma referencia Foo, porem um objeto do tipo Bar. Nesta hora o objeto foo nao encherga a variavel “a” declarada na classe Bar. É como voce se declarasse um metodo na classe filha que nao existe na classe pai, sendo assim o objeto foo tambem nao enxergará esse metodo novo. Na hora da compilaçao o contrutor da classe Bar chama o construtor da classe Foo atribuindo o valor 3 a variavel a da classe Foo. Como na herança, tudo que estiver na classe pai é herdada pela filha, caso esteja marcada com os modificadores corretos. Por isso foo.a é mostrado com o valor 3.