5.4 Vendendo diferentes tipos de Livro

Para dar mais um passo em nossa aplicação, criaremos agora uma classe RegistroDeVendas, por enquanto com um método main e o seguinte conteúdo:

public class RegistroDeVendas {
public static void main(String[] args) {

LivroFisico fisico = new livroFisico(autor); fisico.setNome(“Test-Driven Development”);

Ebook ebook = new Ebook(autor); ebook.setNome(“Test-Driven Development”);

}

}

Repare que criamos um LivroFisico e um Ebook com o mesmo Autor. Nosso próximo passo será adicionar esses dois elementos em um CarrinhoDeCompras. O código deve ficar parecido com este:

CarrinhoDeCompras carrinho = new carrinhoDeCompras(); carrinho.adiciona(fisico);

carrinho.adiciona(ebook);

Vamos agora criar a classe CarrinhoDeCompras e seu método adiciona. Como vamos adicionar LivroFisicose também Ebooks, uma forma de fazer isso seria criando dois métodos adiciona, um para cada tipo de Livro (uma sobrecarga):

public class CarrinhoDeCompras {
public void adiciona(LivroFisico livro) { System.out.println("Adicionando: " + livro);

}

public void adiciona(Ebook livro) { System.out.println("Adicionando: " + livro);

}

}

Esse código funcionaria sem nenhum problema, mas repare que está

muito repetido. Além disso, a cada novo tipo de Livro, precisaremos criar um novo método adiciona que receba esse tipo como parâmetro, o que seria um tanto trabalhoso e difícil de manter.

Para evitar isso, podemos utilizar um recurso da linguagem bastante útil e poderoso. Como existe uma herança envolvida, podemos dizer que tanto um LivroFisico como um Ebook são filhos (extensões) da classe Livro. Poderíamos criar um único método adiciona, que recebe um Livro (superclasse) como parâmetro:

public class CarrinhoDeCompras{ public void adiciona(Livrolivro) {
System.out.println("Adicionand:o" + livro);

}

}

Nosso c6digo compilara e funcionara como esperado, pois podemos nos referenciar a esses objetos dessa forma mais generica, pela sua classe pai. Esse interessante recurso e conhecido como Po l i mo r f i s mo.

Veja o que acontece quando executamos nossa classe RegistroDeVendas adicionando os dois tipos de Li vr o no CarrinhoDeCompras, que agora possui um unico metodo a d i c i o na :

Adicionado: LivroFisico@4f23c55 Adicionado: Ebook@2la33b44

Como estamos imprimindo o objeto inteiro e nao um de seus atrib­ utos, o comportamento padrao e mostrar o nome da classe mais um @codigoEstranho. Entenderemos a fundo esse comportamento mais a frente, mas o importante agora e perceber que o objeto passado continua

sendo um Li vr o Fi s i c o ou Ebook, apenas a forma como nos referimos a ele e que mudou.

Não estamos transformando objetos

Lembre-se que uma variável guarda uma referência para um objeto, e não um objeto em si. Até agora, estávamos sempre declarando um Ebook como tipo Ebook. Repare:

Ebook ebook = new Ebook();

Mas, como vimos, também podemos dizer que um Ebook é do tipo

Livro, afinal ele herda (é um) Livro.

Livro ebook = new Ebook();

Mas é fundamental perceber que não estamos transformando esse objeto. Se criamos um Ebook, ele será um Ebook e ponto. Estamos apenas nos referenciando a ele como um Livro, uma abstração.

Perceba que, como estamos referenciando o parâmetro passado para o método adiciona da classe CarrinhoDeCompras como um Livro, apenas os métodos presentes na classe Livro poderão ser invocados sem que um erro de compilação ocorra.

Para ficar mais claro, ainda que passando um objeto do tipo Ebook para o adiciona, ao tentar invocar seu método getWaterMark, o seguinte erro de compilação ocorrerá:

The method getWaterMark() is undefined for the type Livro

Faz sentido. Para que isso funcione, precisaríamos fazer um casting

moldando o parâmetro livro parao tipo Ebook:

public void adiciona(Livro livro) { Ebook ebook = (Ebook) livro; ebook.getWaterMark();

⬅️ Capítulo Anterior | Próximo Capítulo ➡️