Usando polimorfismo com métodos privados

Bom, tenho o código abaixo, que tem a saída: “high high”

public class A{

  private String runNow(){
    return "High";
  }

  static class B extends A{
	  
    public String runNow(){
      return "Low";
    }
  }

  public static void main(String args[]){
    A[] a=new B[]{new B(),new C()};
    for(A aa:a)
      System.out.print(aa.runNow()+" ");
  }

}



class C extends A.B{

  public String runNow(){
    return "Out";
  }

}

Eu imagino que o motivo seja o seguinte (Me corrijam se eu estiver errado) : Quando tentamos chamar o método “aa.runNow()” do objeto B, o compilador começa procurando pela superclasse (no caso a classe A) e como verifica que na classe A tem um método runNow() PRIVADO, ele já deduz que não haverá nenhum “override” para este método nas subclasses, por isso executa logo o runNow() da classe A.

O mesmo ocorre quando executamos o runNow() do objeto C, ele vai lá na classe A e verifica que o runNow() é privado e que não pode ser sobrescrito e executa logo. Ou seja, o conceito de polimorfismo não se aplica aqui, já que o runNow() da classe A é privado.

Ne verdade é sempre bom olhar o lado esquerdo da atribuição, ou nesse caso as referências criadas:

Nessa linha:

A[] a=new B[]{new B(),new C()};

Você está dizendo que tanto new B(), quanto new C() são A, e de fato o são.

Agora para que ele possa identificar os métodos runNow() proprio de cada filho de A, é necessário cast pois B e C sabem que são filhos de A, mas A não sabe que B e C são seus filhos, logo, não sabe da existência de runNow() neles, mas sim dele próprio.

Quanto a questão do método ser privado em A, tenha em mente que para outras classes ele não é acessível, você consegue executá-lo em main por rodar a aplicação na própria classe.

É para casos como esse que servem métodos abstratos na classe pai, aliás, sugiro você modificar sua classe A para ter o runNow() dessa forma:

public abstract class A{  
  
  private abstract runNow();
}

Creio que com isso você consegue resolver o problema, mas tenha em mente que o polimorfismo no primeiro caso ocorreu normalmente.

Mas a questão aqui é entender o seguinte: Porque quando eu chamo o runNow() da classe B ele executa o runNow() da classe A ? Minha ideia é que ele sempre começa procurando pelo método na superclasse, e como o mesmo é privado ele executa-o, caso contrário o método da classe B é executado.

Negativo, vou tentar explicar de outra maneira:

Quando se declara um array do tipo A, ele só aceita elementos do tipo A confere? Sendo assim B e C também são tipo A.

Agora ele sempre busca o método runNow da classe A simplesmente por causa disso:

A[] a = ...

O java se baseia na referência para identificar os métodos que a classe possui, como você declarou runNow() concreto (com corpo para ser executado) ele irá executá-lo logo no primeiro que encontrou (o que é o lógico, pois se existe um método concreto na classe A, porque não executá-lo?), então ele nem perde tempo examinando quem está do outro lado da atribuição.

Por isso sugeri trocar sua classe para abstract e seu método runNow() também, pois isso força a jvm parar e pensar: “Opa, o programador declarou o método runNow() na classe A como abstrato… quer dizer que tem algum filho dele rodando esse método…”.

Como base nessa linha de pensamento a jvm pensa de novo: “Vou ver do outro lado da atribuição quais elementos tem esse método runNow() para eu executar…”

Pra ser sincero os exemplo com classes de nomes A, B, C confundem mais ainda, posso sugerir outro exemplo? Diga no seu próximo post se posso exemplificá-lo.

Amigo, é que esse exemplo é de uma questão da OCJP. Por isso quero tentar entendê-lo com exatidão. Mas vamos lá… (pode sugerir outro exemplo também, não tem problema)

Quando você disse “o que é o lógico, pois se existe um método concreto na classe A, porque não executá-lo?” eu penso que mesmo que tenha uma método concreto na classe A, pode haver outro método concreto na classe B (filha de A), então o correto seria ele executar o método da classe B por estar sobrescrevendo a classe A.

Olá
No caso não está “overridando” (para verificar basta testar com @Override).
Este é um caso de sobrecarga, está sendo criado um novo método com a mesma assinatura.
C está overridando B, para testar basta mudar de A[] para B[] na atribuição e no for