Olá! A razão pela qual você não pode acessar o método latir() ao utilizar a referência do tipo Animal para um objeto do tipo Cachorro é devido ao conceito de polimorfismo e à natureza da referência em tempo de compilação.
Quando você declara a referência Animal cao1 = new Cachorro();, você está criando uma referência do tipo Animal que aponta para um objeto do tipo Cachorro. Em tempo de compilação, o compilador só conhece os membros (métodos e campos) da classe Animal, mesmo que o objeto real seja do tipo Cachorro. Portanto, a referência cao1 só permite acessar membros da classe Animal.
Para resolver isso, você pode fazer um downcasting, convertendo explicitamente a referência de Animal para Cachorro:
Animal cao1 = new Cachorro();
((Cachorro) cao1).latir();
No entanto, ao usar downcasting, você deve ter cuidado para garantir que o objeto referenciado seja realmente uma instância de Cachorro em tempo de execução, para evitar exceções do tipo ClassCastException. Portanto, é uma boa prática verificar se a conversão é segura usando o operador instanceof:
Animal cao1 = new Cachorro();
if (cao1 instanceof Cachorro) {
((Cachorro) cao1).latir();
} else {
System.out.println("O objeto não é um Cachorro.");
}
Em geral, é aconselhável usar polimorfismo e trabalhar com referências do tipo da superclasse sempre que possível, mas, quando necessário, você pode usar downcasting com cuidado.