(→Introdução) |
|||
(51 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | {{NAVPO}} | |
+ | {{TOCright}} | ||
− | + | A linguagem Java dispõe de um conjunto de conceitos (classes) que permite tratar as construções da linguagem (tipos, métodos) como objectos. Estes objectos são objectos "normais", no sentido de que possuem estado e apresentam uma interface que permite requisitar operações em tempo de execução. Exemplos são o pedido de listas de construtores a classes, ou o pedido de criação de instâncias. É possível, através deste mecanismo, a escrita de código muito flexível ao nível do tratamento de classes, inclusivamente sem necessidade de parar uma aplicação que esteja em execução. A desvantagem principal é partilhada com mecanismos de função semelhante noutras linguagens: o código não é, em geral, directamente portável. | |
− | + | ||
− | + | Este conjunto de classes está globalmente reunido na ''package'' '''java.lang.reflect''' ("reflection" é um termo geralmente usado para referir a capacidade da linguagem se auto-descrever nestes aspectos). | |
− | + | ||
+ | Das classes que se abordam abaixo, talvez a mais importante, pois é a partir dela que surgem as outras utilizações, é a classe '''Class'''. São instâncias desta classe objectos que representam as classes, interfaces e enumerados da linguagem. Isto inclui a própria classe '''Class''' (note-se que existe a interface '''Type''', mas não é directamente útil na generalidade dos casos). | ||
+ | |||
+ | Esta página não quer substituir o manual da linguagem Java, relativamente a este aspecto, e ilustra apenas como o mecanismo pode ser utilizado produtivamente. | ||
+ | O manual da linguagem e as descrições das classes principais e auxiliares (principalmente excepções), podem ser consultados aqui: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/reflect/package-summary.html | ||
+ | |||
+ | == Como obter instâncias da classe "Class"? == | ||
+ | |||
+ | Não é possível instanciar directamente esta classe. | ||
+ | |||
+ | A obtenção de instâncias faz-se atravês de três processos: propriedade '''class''' das classes; propriedade '''TYPE''' das classes associadas a tipos primitivos; método '''Class.forName(String)'''. | ||
+ | |||
+ | O seguinte exemplo obtém os objectos que descrevem os tipos associados com o conceito de número inteiro em Java. Recorda-se que o método '''println''' está a utilizar o resultado da invocação implícita de '''toString''' sobre os tipos indicados. | ||
+ | <source lang="java"> | ||
+ | public class SimpleTest { | ||
+ | public static void main(String[] args) { | ||
+ | Class<?> type1 = Integer.class; | ||
+ | Class<?> type2 = Integer.TYPE; | ||
+ | System.out.println(type1); | ||
+ | System.out.println(type2); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | O resultado é: | ||
+ | '''$ java SimpleTest''' | ||
+ | class java.lang.Integer | ||
+ | int | ||
== Exemplo == | == Exemplo == | ||
Line 10: | Line 38: | ||
Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos. | Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos. | ||
− | Considere-se o ficheiro < | + | === Introdução === |
+ | Considere-se o ficheiro '''obras.txt''', contendo descrições de DVDs (título e realizador) e de Livros (título, autor e [[wikipedia:ISBN|ISBN]]): | ||
+ | DVD:Era uma vez na Amadora:Fernando Fonseca | ||
+ | DVD:Lumiar selvagem:Pedro Fonseca | ||
+ | Livro:A arte de sobreviver no 36:João Fonseca:1234567890 | ||
+ | Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 | ||
+ | DVD:48 horas para o exame:Orlando Fonseca | ||
+ | Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 | ||
+ | |||
+ | A ideia da aplicação que se descreve de seguida é a leitura de cada linha como se da criação de um objecto se tratasse. Assim, o primeiro campo (os campos são separados por "''':'''") define a classe do objecto e os restantes campos são passados como argumentos ao constructor da classe. Pretende-se ainda que a implementação seja suficientemente flexível para que resista à utilização de descrições erradas ou de descrições de objectos desconhecidos. | ||
+ | |||
+ | === A classe de base === | ||
+ | |||
+ | A classe de base de todas as entidades a criar é '''Obra'''. Esta classe, por um lado, impõe às suas subclasses a definição do método '''processa''' e, por outro, recorrendo a uma fábrica simples que, fazendo uso de informação de tipos em tempo de execução, permite criar instâncias das suas subclasses, de acordo com uma descrição passada como argumento ('''cria'''). | ||
+ | |||
+ | Os blocos declarados '''static''' numa classe correspondem a código que é executado quando a classe é carregada pela máquina virtual. | ||
+ | |||
+ | {{CollapsedCode|Ficheiro Obra.java| | ||
+ | <source lang="java"> | ||
+ | import java.util.ArrayList; | ||
+ | import java.lang.reflect.Constructor; | ||
+ | import java.lang.reflect.InvocationTargetException; | ||
+ | |||
+ | public abstract class Obra { | ||
+ | static { System.out.println("Carregamento da classe Obra"); } | ||
+ | |||
+ | public abstract void processa(); | ||
+ | |||
+ | static Obra cria(String dsc) { | ||
+ | String dados[] = dsc.split(":"); | ||
+ | try { | ||
+ | Class<?> tipo = Class.forName(dados[0]); | ||
+ | ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1); | ||
+ | for (int ix = 1; ix < dados.length; ix++) | ||
+ | ctorargs.add(ix-1, dados[ix]); | ||
+ | Constructor<?> ctor = tipo.getConstructors()[0]; // hack? só existe um... | ||
+ | return (Obra)ctor.newInstance(ctorargs.toArray()); | ||
+ | } | ||
+ | catch (ClassNotFoundException e) { // forName | ||
+ | System.err.println("!! TIPO DE OBRA DESCONHECIDO !!"); | ||
+ | System.err.println(e); | ||
+ | return null; | ||
+ | } | ||
+ | catch (InstantiationException e) { // newInstance | ||
+ | System.err.println("!! TIPO DE OBRA ABSTRACTO !!"); | ||
+ | System.err.println(e); | ||
+ | return null; | ||
+ | } | ||
+ | catch (IllegalAccessException e) { // newInstance | ||
+ | System.err.println("!! TIPO DE OBRA SEM CONSTRUCTOR ACESSÃVEL!!"); | ||
+ | System.err.println(e); | ||
+ | return null; | ||
+ | } | ||
+ | catch (IllegalArgumentException e) { // newInstance | ||
+ | System.err.println("!! TIPO DE OBRA MAL DESCRITO !!"); | ||
+ | System.err.println(e); | ||
+ | return null; | ||
+ | } | ||
+ | catch (InvocationTargetException e) { // newInstance | ||
+ | System.err.println("!! TIPO DE OBRA COM CONSTRUCTOR EM APUROS !!"); | ||
+ | System.err.println(e); | ||
+ | return null; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | Note-se o tratamento de várias excepções, em particular, o tratamento da excepção '''ClassNotFoundException''', que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual. | ||
+ | |||
+ | Note-se ainda o tratamento de '''InvocationTargetException''', que permite lidar com as excepções específicas do constructor da obra em causa (''exception chaining''). | ||
+ | |||
+ | === As classes das obras === | ||
+ | |||
+ | Para DVDs: | ||
+ | |||
+ | {{CollapsedCode|Ficheiro DVD.java| | ||
+ | <source lang="java"> | ||
+ | public class DVD extends Obra { | ||
+ | static { System.out.println("Carregamento da classe DVD"); } | ||
+ | |||
+ | String _título; | ||
+ | String _realizador; | ||
+ | |||
+ | public DVD(String título, String realizador) { | ||
+ | _título = título; | ||
+ | _realizador = realizador; | ||
+ | } | ||
+ | |||
+ | public String toString() { return "DVD:" + _título + ":" + _realizador; } | ||
+ | public void ler() { System.out.println("LER " + this); } | ||
+ | public void rodopiar() { System.out.println("RODOPIAR " + this); } | ||
+ | |||
+ | public void processa() { rodopiar(); ler(); } | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | Para livros: | ||
+ | |||
+ | {{CollapsedCode|Ficheiro Livro.java| | ||
+ | <source lang="java"> | ||
+ | public class Livro extends Obra { | ||
+ | static { System.out.println("Carregamento da classe Livro"); } | ||
+ | |||
+ | String _título; | ||
+ | String _autor; | ||
+ | String _isbn; | ||
+ | |||
+ | public Livro(String título, String autor, String isbn) { | ||
+ | _título = título; | ||
+ | _autor = autor; | ||
+ | _isbn = isbn; | ||
+ | } | ||
+ | |||
+ | public String toString() { return "Livro:" + _título + ":" + _autor + ":" + _isbn; } | ||
+ | public void ler() { System.out.println("LER " + this); } | ||
+ | public void folhear() { System.out.println("FOLHEAR " + this); } | ||
+ | |||
+ | public void processa() { folhear(); ler(); } | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Aplicação exemplo === | ||
+ | |||
+ | Esta aplicação lê o ficheiro de obras (propriedade '''obras''') e processa cada linha, criando os objectos correspondentes. Depois de lidas, as obras são processadas (uniformemente). | ||
+ | |||
+ | {{CollapsedCode|Ficheiro Teste.java| | ||
+ | <source lang="java"> | ||
+ | import java.util.ArrayList; | ||
+ | import java.io.BufferedReader; | ||
+ | import java.io.FileReader; | ||
+ | import java.io.IOException; | ||
+ | |||
+ | public class Teste { public static void main(String[] args) { | ||
+ | String entrada = System.getProperty("obras", "obras.txt"); | ||
+ | try (var r = new BufferedReader(new FileReader(entrada))) { | ||
+ | String linha; | ||
+ | var obras = new ArrayList<Obra>(); | ||
+ | while ((linha = r.readLine()) != null) { | ||
+ | Obra obra = Obra.cria(linha); | ||
+ | obras.add(obra); | ||
+ | System.out.println(obra); | ||
+ | } | ||
+ | System.out.println("****************"); | ||
+ | for (var obra: obras) obra.processa(); | ||
+ | } | ||
+ | catch (IOException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | Note-se que a função '''main''' declara o lançamento de excepções. Esta prática não é ideal (a função deveria tratar as excepções) e apenas é usada aqui por simplicidade de exposição. | ||
+ | |||
+ | A saída da aplicação de teste é a que se apresenta. Note-se a ordem de carregamento das classes. | ||
+ | |||
+ | '''% java -Dobras=obras.txt Teste''' | ||
+ | <font color="green">Carregamento da classe Obra</font> | ||
+ | <font color="green">Carregamento da classe DVD</font> | ||
+ | DVD:Era uma vez na Amadora:Fernando Fonseca | ||
+ | DVD:Lumiar selvagem:Pedro Fonseca | ||
+ | <font color="green">Carregamento da classe Livro</font> | ||
+ | Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890 | ||
+ | Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 | ||
+ | DVD:48 horas para o exame:Orlando Fonseca | ||
+ | Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 | ||
+ | **************** | ||
+ | RODOPIAR DVD:Era uma vez na Amadora:Fernando Fonseca | ||
+ | LER DVD:Era uma vez na Amadora:Fernando Fonseca | ||
+ | RODOPIAR DVD:Lumiar selvagem:Pedro Fonseca | ||
+ | LER DVD:Lumiar selvagem:Pedro Fonseca | ||
+ | FOLHEAR Livro:A arte de sobreviver no 36:João Fonseca:1234567890 | ||
+ | LER Livro:A arte de sobreviver no 36:João Fonseca:1234567890 | ||
+ | FOLHEAR Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 | ||
+ | LER Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 | ||
+ | RODOPIAR DVD:48 horas para o exame:Orlando Fonseca | ||
+ | LER DVD:48 horas para o exame:Orlando Fonseca | ||
+ | FOLHEAR Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 | ||
+ | LER Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 | ||
+ | |||
+ | [[category:Ensino]] | ||
+ | [[category:PO]] | ||
+ | [[category:PO Exemplos]] | ||
+ | [[category:Java]] |
A linguagem Java dispõe de um conjunto de conceitos (classes) que permite tratar as construções da linguagem (tipos, métodos) como objectos. Estes objectos são objectos "normais", no sentido de que possuem estado e apresentam uma interface que permite requisitar operações em tempo de execução. Exemplos são o pedido de listas de construtores a classes, ou o pedido de criação de instâncias. É possível, através deste mecanismo, a escrita de código muito flexível ao nível do tratamento de classes, inclusivamente sem necessidade de parar uma aplicação que esteja em execução. A desvantagem principal é partilhada com mecanismos de função semelhante noutras linguagens: o código não é, em geral, directamente portável.
Este conjunto de classes está globalmente reunido na package java.lang.reflect ("reflection" é um termo geralmente usado para referir a capacidade da linguagem se auto-descrever nestes aspectos).
Das classes que se abordam abaixo, talvez a mais importante, pois é a partir dela que surgem as outras utilizações, é a classe Class. São instâncias desta classe objectos que representam as classes, interfaces e enumerados da linguagem. Isto inclui a própria classe Class (note-se que existe a interface Type, mas não é directamente útil na generalidade dos casos).
Esta página não quer substituir o manual da linguagem Java, relativamente a este aspecto, e ilustra apenas como o mecanismo pode ser utilizado produtivamente. O manual da linguagem e as descrições das classes principais e auxiliares (principalmente excepções), podem ser consultados aqui: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/reflect/package-summary.html
Não é possível instanciar directamente esta classe.
A obtenção de instâncias faz-se atravês de três processos: propriedade class das classes; propriedade TYPE das classes associadas a tipos primitivos; método Class.forName(String).
O seguinte exemplo obtém os objectos que descrevem os tipos associados com o conceito de número inteiro em Java. Recorda-se que o método println está a utilizar o resultado da invocação implícita de toString sobre os tipos indicados.
public class SimpleTest {
public static void main(String[] args) {
Class<?> type1 = Integer.class;
Class<?> type2 = Integer.TYPE;
System.out.println(type1);
System.out.println(type2);
}
}
O resultado é:
$ java SimpleTest class java.lang.Integer int
Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos.
Considere-se o ficheiro obras.txt, contendo descrições de DVDs (título e realizador) e de Livros (título, autor e ISBN):
DVD:Era uma vez na Amadora:Fernando Fonseca DVD:Lumiar selvagem:Pedro Fonseca Livro:A arte de sobreviver no 36:João Fonseca:1234567890 Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 DVD:48 horas para o exame:Orlando Fonseca Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892
A ideia da aplicação que se descreve de seguida é a leitura de cada linha como se da criação de um objecto se tratasse. Assim, o primeiro campo (os campos são separados por ":") define a classe do objecto e os restantes campos são passados como argumentos ao constructor da classe. Pretende-se ainda que a implementação seja suficientemente flexível para que resista à utilização de descrições erradas ou de descrições de objectos desconhecidos.
A classe de base de todas as entidades a criar é Obra. Esta classe, por um lado, impõe às suas subclasses a definição do método processa e, por outro, recorrendo a uma fábrica simples que, fazendo uso de informação de tipos em tempo de execução, permite criar instâncias das suas subclasses, de acordo com uma descrição passada como argumento (cria).
Os blocos declarados static numa classe correspondem a código que é executado quando a classe é carregada pela máquina virtual.
Ficheiro Obra.java |
---|
import java.util.ArrayList;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public abstract class Obra {
static { System.out.println("Carregamento da classe Obra"); }
public abstract void processa();
static Obra cria(String dsc) {
String dados[] = dsc.split(":");
try {
Class<?> tipo = Class.forName(dados[0]);
ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1);
for (int ix = 1; ix < dados.length; ix++)
ctorargs.add(ix-1, dados[ix]);
Constructor<?> ctor = tipo.getConstructors()[0]; // hack? só existe um...
return (Obra)ctor.newInstance(ctorargs.toArray());
}
catch (ClassNotFoundException e) { // forName
System.err.println("!! TIPO DE OBRA DESCONHECIDO !!");
System.err.println(e);
return null;
}
catch (InstantiationException e) { // newInstance
System.err.println("!! TIPO DE OBRA ABSTRACTO !!");
System.err.println(e);
return null;
}
catch (IllegalAccessException e) { // newInstance
System.err.println("!! TIPO DE OBRA SEM CONSTRUCTOR ACESSÃVEL!!");
System.err.println(e);
return null;
}
catch (IllegalArgumentException e) { // newInstance
System.err.println("!! TIPO DE OBRA MAL DESCRITO !!");
System.err.println(e);
return null;
}
catch (InvocationTargetException e) { // newInstance
System.err.println("!! TIPO DE OBRA COM CONSTRUCTOR EM APUROS !!");
System.err.println(e);
return null;
}
}
}
|
Note-se o tratamento de várias excepções, em particular, o tratamento da excepção ClassNotFoundException, que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual.
Note-se ainda o tratamento de InvocationTargetException, que permite lidar com as excepções específicas do constructor da obra em causa (exception chaining).
Para DVDs:
Ficheiro DVD.java |
---|
public class DVD extends Obra {
static { System.out.println("Carregamento da classe DVD"); }
String _título;
String _realizador;
public DVD(String título, String realizador) {
_título = título;
_realizador = realizador;
}
public String toString() { return "DVD:" + _título + ":" + _realizador; }
public void ler() { System.out.println("LER " + this); }
public void rodopiar() { System.out.println("RODOPIAR " + this); }
public void processa() { rodopiar(); ler(); }
}
|
Para livros:
Ficheiro Livro.java |
---|
public class Livro extends Obra {
static { System.out.println("Carregamento da classe Livro"); }
String _título;
String _autor;
String _isbn;
public Livro(String título, String autor, String isbn) {
_título = título;
_autor = autor;
_isbn = isbn;
}
public String toString() { return "Livro:" + _título + ":" + _autor + ":" + _isbn; }
public void ler() { System.out.println("LER " + this); }
public void folhear() { System.out.println("FOLHEAR " + this); }
public void processa() { folhear(); ler(); }
}
|
Esta aplicação lê o ficheiro de obras (propriedade obras) e processa cada linha, criando os objectos correspondentes. Depois de lidas, as obras são processadas (uniformemente).
Ficheiro Teste.java |
---|
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Teste { public static void main(String[] args) {
String entrada = System.getProperty("obras", "obras.txt");
try (var r = new BufferedReader(new FileReader(entrada))) {
String linha;
var obras = new ArrayList<Obra>();
while ((linha = r.readLine()) != null) {
Obra obra = Obra.cria(linha);
obras.add(obra);
System.out.println(obra);
}
System.out.println("****************");
for (var obra: obras) obra.processa();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
|
Note-se que a função main declara o lançamento de excepções. Esta prática não é ideal (a função deveria tratar as excepções) e apenas é usada aqui por simplicidade de exposição.
A saída da aplicação de teste é a que se apresenta. Note-se a ordem de carregamento das classes.
% java -Dobras=obras.txt Teste Carregamento da classe Obra Carregamento da classe DVD DVD:Era uma vez na Amadora:Fernando Fonseca DVD:Lumiar selvagem:Pedro Fonseca Carregamento da classe Livro Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890 Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 DVD:48 horas para o exame:Orlando Fonseca Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 **************** RODOPIAR DVD:Era uma vez na Amadora:Fernando Fonseca LER DVD:Era uma vez na Amadora:Fernando Fonseca RODOPIAR DVD:Lumiar selvagem:Pedro Fonseca LER DVD:Lumiar selvagem:Pedro Fonseca FOLHEAR Livro:A arte de sobreviver no 36:João Fonseca:1234567890 LER Livro:A arte de sobreviver no 36:João Fonseca:1234567890 FOLHEAR Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 LER Livro:Bairro Alto e o budismo Zen:Zun Tse Fonseca:1234567891 RODOPIAR DVD:48 horas para o exame:Orlando Fonseca LER DVD:48 horas para o exame:Orlando Fonseca FOLHEAR Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892 LER Livro:Lux e o insucesso escolar - uma visão matemática:Carlos Alberto Fonseca:1234567892