O Geek que sabia Java Parte III
Na terceira parte de nosso curso de Java, abordamos o conceito de hierarquia entre classes. Esta é uma aula essencialmente prática, portanto preste muita atenção ao código fonte e aos blocos de comentário entre ele, que dão dicas do que está sendo feito.
Exemplo de uma Hierarquia de Classes. Compile o exemplo a seguir da seguinte forma:
$ javac Livro.java
$ javac LivroTecnico.java
$ javac LivroInfantil.java
As classes LivroTecnico e LivroInfantil apresentam o método main. Esse método define que uma classe pode ser acessada pela Máquina Virtual Java para iniciar a execução de uma aplicação. Toda a classe que tem declarada esse método pode ser passada como parâmetro para o executável java, tal como a seguir:
$ java LivroTecnico
$ java LivroInfantil
Livro.java
class Livro extends Object
{
// atributos acessados somente
// dentro da classe
private int codigo = 0;
// atributo acessado dentro da classe
// e pelas classes
// que herdam desta classe
protected String nome = null;
// acessível por outros objetos diretamente
public String editora = null;
// acessível por classes pertencentes
// a este pacote
int ano = 0;
public Livro()
{
codigo = 0;
nome = "default";
editora = "default";
ano = 2001;
}
public Livro(int codigo, String nome)
{
this.codigo = codigo;
this.nome = nome;
editora = "default";
ano = 2001;
}
public Livro(String nome, int codigo)
{
this.codigo = codigo;
this.nome = nome;
}
public Livro(int codigo, String nome, String editora, int ano)
{
this.codigo = codigo;
this.nome = nome;
this.editora = editora;
this.ano = ano;
}
public void setCodigo(int codigo)
{
this.codigo = codigo;
}
public void setNome(String nome)
{
this.nome = nome;
}
public void setEditora(String editora)
{
this.editora = editora;
}
public void setAno(int ano)
{
this.ano = ano;
}
public int getCodigo()
{
return codigo;
}
public String getNome()
{
return nome;
}
public String getEditora()
{
return editora;
}
public int getAno()
{
return ano;
}
public void imprime()
{
System.out.println("Livro => "+codigo+", "+nome+", "+editora+", "+ano);
}
}
LivroTecnico.java
{
protected String palavras_chave = null;
public LivroTecnico()
{
// chama o construtor da classe
// pai (superclasse)sem parâmetros
super();
palavras_chave = "sem palavras-chave";
}
public LivroTecnico(int codigo, String nome, String editora, int ano, String palavras_chave)
{
super(codigo, nome, editora, ano);
this.palavras_chave = palavras_chave;
}
public void testeMensagens()
{
Comentário: acessar private int código da classe-pai gera erros, pois um atributo private somente é acessível pela própria classe. Mesmo herdando da classe que tem o atributo private, não é possível acessá-lo diretamente. Todo atributo protected é acessível pela própria classe e pelas classes que herdarem da classe que os contém.
System.out.println("valor de protected String nome -> "+nome);
Comentário: acessando public String editora. Quando um atributo ou método é public ele pode ser acessado diretamente por qualquer objeto.
System.out.println("valor de public String editora -> "+editora);
Comentário: acessando int ano. Neste caso não há modificadores, qualquer classe-filha ou pertencente ao mesmo pacote pode acessá-la.
System.out.println("valor de int ano-> "+ano);
// acessando protected String palavras_chave
System.out.println("protected String palavras_chave -> "+palavras_chave);
}
public void imprime()
{
System.out.println("LivroTecnico");
System.out.println(nome+", "+editora+", "+ano+", "+palavras_chave);
}
public static void main(String args[])
{
LivroTecnico lt = new LivroTecnico(1, "Linguagens", "Novatec", 2001, "c, c++, java, orientação a objetos");
lt.testeMensagens();
Livro lv = new Livro(5, "O Livro Externo", "A Editora", 1930);
Comentário: acessando de forma externa, ou seja, o envio da mensagem para o objeto não é feito dentro da classe System.out.println("Acessando o atributo private int codigo => "+lv.codigo); Isto não funciona, pois private somente é acessível dentro da classe que declara o atributo.
System.out.println("Acessando o atributo protected String nome => "+lv.nome);
System.out.println("Acessando o atributo public editora => "+lv.editora);
// é acessível pois está no mesmo pacote!
System.out.println("Acessando atributo int ano => "+lv.ano);
}
}
LivroInfantil.java
public class LivroInfantil extends Livro
{
private String colecao = null;
public LivroInfantil(int codigo, String nome, String editora, int ano, String colecao)
{
super(codigo, nome, editora, ano);
this.colecao = colecao;
}
public void imprime()
{
System.out.println("LivroInfantil");
System.out.println(nome+", "+editora+", "+ano+", "+colecao);
}
public static void main (String args[])
{
LivroTecnico lt = new LivroTecnico(1, "Tutorial Java", "Sun Microsystems", 1992, "java, orientação a objetos, pacotes");
LivroInfantil li = new LivroInfantil(2, "Branca de Neve", "Editora Infantil", 1931, "princesa, anões");
System.out.println("Criando um objeto LivroTecnico e outro LivroInfantil");
lt.imprime();
li.imprime();
// o \n dentro do System.out.println
// faz pular uma linha
System.out.println("\n\nFazendo um Cast do Objeto LivroTecnico para Livro");
Livro lv1 = (Livro) lt;
lv1.imprime();
System.out.println("\n\nFazendo um
//Cast do Objeto LivroInfantil para Livro");
Livro lv2 = (Livro) li;
lv2.imprime();
// System.out.println("\n\nForçando
// um Cast de LivroInfantil
// para Livro Tecnico");
// System.out.println("Este Cast é fora da // hierarquia de classes");
// System.out.println("Portanto não
// funciona!"); LivroInfantil li1 = new
// LivroInfantil(1, "livro infantil",
// "editora de livros", 1998,
// "coleção infantil");
// LivroTecnico tc1 = (LivroTecnico) li1;
System.out.println("\n\nFazendo um cast do Pai para o Filho...");
try
{
LivroTecnico _lt = (LivroTecnico) new Livro(7, "o livro", "a editora", 2001);
_lt.imprime();
}
catch(Exception e)
{
System.out.println("Não foi possível fazer o cast...");
}
Comentário: Este cast de Pai para Filho não funciona, pois o que existe no filho não existe no pai, entretanto o que existe no pai existe no filho. Assim, pode-se converter um filho no pai, mas não o contrário. Isto é possível, pois na herança todo o código do pai foi passado para o filho. Entretanto converter o pai no filho gera erros, pois o pai não contém todo o código do filho.
}
}
Hierarquia, palavra-chave this e Escopo
No exemplo desta seção são aplicados conceitos de hierarquia de classes, uso da palavra-chave this para acesso aos identificadores membro da classe e apresentados conceitos de escopo de forma prática. Esse exemplo é formado pela superclasse Veiculo, que é genérica, como toda superclasse deve ser. As especializações da superclasse Veiculo são feitas na implementação das subclasses Moto e Carro.
Para compilar o exemplo a seguir proceda da seguinte forma:
$ javac Veiculo.java
$ javac Moto.java
$ javac Carro.java
A classe Moto apresenta o método main que possibilita a execução do aplicativo da seguinte forma:
$ java Moto
No código-fonte desse exemplo apresentado a seguir existem diversas explicações sobre o uso de hierarquia, escopo e da palavra-chave this.
Veiculo.java
class Veiculo extends Object
{
private int ano = 0;
protected String marca = null;
public Veiculo()
{
// acessando variável ano da classe, pois
// não há nenhuma variável ano definida
// no escopo deste método
ano = 2001;
}
public Veiculo(int ano)
{
// this é o mesmo que um ponteiro para uma
// instância do objeto, o ponto (.)
this.ano = ano;
Comentário: quando tentamos acessar uma variável ano o que ocorre? Se esta variável estiver definida dentro do método no qual se está chamando você estará se referindo à variável ano no escopo do método. Portanto, quando nos referimos a ano dentro deste construtor, estamos falando da variável ano definida dentro deste método, ou seja, aquela variável definida como parâmetro do construtor public Veiculo(int ano). Caso a variável ano do construtor não esteja definida estaremos acessando o atributo ano da classe, como no construtor vazio acima!
}
public void setAno(int ano)
{
// atribuindo o valor da variável ano para
// o atributo de classe ano
this.ano = ano;
}
public int getAno()
{
return ano;
}
}
Moto.java
class Moto extends Veiculo
{
public int cilindradas = 0;
public Moto()
{
super();
cilindradas = 125;
}
public Moto(int cilindradas)
{
super();
this.cilindradas = cilindradas;
}
public Moto(int ano, String marca, int cilindradas)
{
super(ano);
Comentário: O código abaixo acessa o atributo protected marca definido na classe-pai atribuindo-lhe o valor da variável marca definida neste construtor.
this.marca = marca;
this.cilindradas = cilindradas;
}
public static void main (String args[])
{
Moto x = new Moto();
System.out.println(x.cilindradas);
}
}
Carro.java
class Carro extends Veiculo
{
protected int numero_portas = 0;
public Carro()
{
// chamando o construtor do pai com
// parâmetro vazio!
super();
// atributo marca acessível, pois está
// definido com protected na classe-pai
marca = "sem marca";
numero_portas = 2;
}
public Carro(int ano, String marca, int numero_portas)
{
// chamando contrutor com parâmetro
// do ano do veículo
super(ano);
// atribuindo o valor da variável marca
// declarada no construtor para o atributo
// de classe this.marca
this.marca = marca;
// atribuindo variável numero_portas para
// o atributo this.numero_portas da classe
this.numero_portas = numero_portas;
}
public void setNumeroPortas(int numero_portas)
{
this.numero_portas = numero_portas;
}
public int getNumeroPortas()
{
return numero_portas;
}
}
Na próxima edição, abordaremos os conceitos relacionados ao desenvolvimento de aplicações orientadas a objetos: Levantamento e análise dos requisitos, estudo de caso do sistema, estudo da hierarquia entre as classes e análise da arquitetura. Até lá!
Rodrigo Fernandes de Mello - mello@virgos.com.br
É um dos autores do livro 'Aprendendo Java 2', publicado pela Novatec Editora