Difference between revisions of "Informação de Tipos em Tempo de Execução (Java)"

From Wiki**3

(A classe de base)
Line 56: Line 56:
 
A classe de base de todas as entidades a criar é <tt>Obra</tt>. Esta classe, por um lado, impõe às suas subclasses a definição do método <tt>processa</tt> 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 (<tt>cria</tt>).
 
A classe de base de todas as entidades a criar é <tt>Obra</tt>. Esta classe, por um lado, impõe às suas subclasses a definição do método <tt>processa</tt> 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 (<tt>cria</tt>).
  
  <b>import</b><span style="color: #808000;"> java.util.ArrayList;</span>
+
<java5>
  <b>import</b><span style="color: #808000;"> java.lang.reflect.Constructor;</span>
+
import java.util.ArrayList;
  <b>import</b><span style="color: #808000;"> java.lang.reflect.InvocationTargetException;</span>
+
import java.lang.reflect.Constructor;
   
+
import java.lang.reflect.InvocationTargetException;
  <b>public abstract class</b> Obra {
+
 
    <span style="color: #800000;">static</span> { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"Carregamento da classe Obra"</span>); }
+
  public abstract class Obra {
+
  static { System.out.println("Carregamento da classe Obra"); }
    <b>public abstract</b> <span style="color: #800000;">void</span> <span style="color: #000080;">processa</span>();
+
 
+
  public abstract void processa();
    <span style="color: #800000;">static</span> Obra <span style="color: #000080;">cria</span>(<span style="font-weight: bold;color: #0095ff;">String</span> dsc) {
+
 
      <span style="font-weight: bold;color: #0095ff;">String</span> dados[] = dsc.<span style="color: #000080;">split</span>(<span style="color: #dd0000;">":"</span>);
+
  static Obra cria(String dsc) {
      <b>try</b> {
+
    String dados[] = dsc.split(":");
        <span style="font-weight: bold;color: #0095ff;">ArrayList</span>&lt;<span style="font-weight: bold;color: #0095ff;">String</span>&gt; ctorargs = <b>new</b> <span style="font-weight: bold;color: #0095ff;">ArrayList</span>&lt;<span style="font-weight: bold;color: #0095ff;">String</span>&gt;(dados.<span style="color: #000080;">length</span>-<span style="color: #0000ff;">1</span>);
+
    try {
        <span style="font-weight: bold;color: #0095ff;">Class</span> tipo = <span style="font-weight: bold;color: #0095ff;">Class</span>.<span style="color: #000080;">forName</span>(dados[<span style="color: #0000ff;">0</span>]);
+
      ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1);
        <b>for</b> (<span style="color: #800000;">int</span> ix = <span style="color: #0000ff;">1</span>; ix &lt; dados.<span style="color: #000080;">length</span>; ix++)
+
      Class tipo = Class.forName(dados[0]);
          ctorargs.<span style="color: #000080;">add</span>(ix-<span style="color: #0000ff;">1</span>, dados[ix]);
+
      for (int ix = 1; ix < dados.length; ix++)
        <span style="font-weight: bold;color: #0095ff;">Constructor</span> ctor = tipo.<span style="color: #000080;">getConstructors</span>()[<span style="color: #0000ff;">0</span>];  <span style="font-style: italic;color: #808080;">// hack? só existe um...</span>
+
        ctorargs.add(ix-1, dados[ix]);
        <b>return</b> (Obra)ctor.<span style="color: #000080;">newInstance</span>(ctorargs.<span style="color: #000080;">toArray</span>());
+
      Constructor ctor = tipo.getConstructors()[0];  // hack? só existe um...
      }
+
      return (Obra)ctor.newInstance(ctorargs.toArray());
      <b>catch</b> (<span style="font-weight: bold;color: #0095ff;">ClassNotFoundException</span> e) {  <span style="font-style: italic;color: #808080;">// forName</span>
+
    }
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"!! TIPO DE OBRA DESCONHECIDO !!"</span>);
+
    catch (ClassNotFoundException e) {  // forName
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(e);
+
      System.err.println("!! TIPO DE OBRA DESCONHECIDO !!");
        <b>return null</b>;
+
      System.err.println(e);
      }
+
      return null;
      <b>catch</b> (<span style="font-weight: bold;color: #0095ff;">InstantiationException</span> e) {  <span style="font-style: italic;color: #808080;">// newInstance</span>
+
    }
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"!! TIPO DE OBRA ABSTRACTO !!"</span>);
+
    catch (InstantiationException e) {  // newInstance
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(e);
+
      System.err.println("!! TIPO DE OBRA ABSTRACTO !!");
        <b>return null</b>;
+
      System.err.println(e);
      }
+
      return null;
      <b>catch</b> (<span style="font-weight: bold;color: #0095ff;">IllegalAccessException</span> e) {  <span style="font-style: italic;color: #808080;">// newInstance</span>
+
    }
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"!! TIPO DE OBRA SEM CONSTRUCTOR ACESSÍVEL!!"</span>);
+
    catch (IllegalAccessException e) {  // newInstance
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(e);
+
      System.err.println("!! TIPO DE OBRA SEM CONSTRUCTOR ACESSÍVEL!!");
        <b>return null</b>;
+
      System.err.println(e);
      }
+
      return null;
      <b>catch</b> (<span style="font-weight: bold;color: #0095ff;">IllegalArgumentException</span> e) {  <span style="font-style: italic;color: #808080;">// newInstance</span>
+
    }
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"!! TIPO DE OBRA MAL DESCRITO !!"</span>);
+
    catch (IllegalArgumentException e) {  // newInstance
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(e);
+
      System.err.println("!! TIPO DE OBRA MAL DESCRITO !!");
        <b>return null</b>;
+
      System.err.println(e);
      }
+
      return null;
      <b>catch</b> (<span style="font-weight: bold;color: #0095ff;">InvocationTargetException</span> e) {  <span style="font-style: italic;color: #808080;">// newInstance</span>
+
    }
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"!! TIPO DE OBRA COM CONSTRUCTOR EM APUROS !!"</span>);
+
    catch (InvocationTargetException e) {  // newInstance
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">err</span>.<span style="color: #000080;">println</span>(e);
+
      System.err.println("!! TIPO DE OBRA COM CONSTRUCTOR EM APUROS !!");
        <b>return null</b>;
+
      System.err.println(e);
      }
+
      return null;
    }
+
    }
   
+
  }
  }
+
 
 +
  }
 +
</java5>
  
 
Note-se o tratamento de várias excepções, em particular, o tratamento da excepção <tt>ClassNotFoundException</tt>, que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual.
 
Note-se o tratamento de várias excepções, em particular, o tratamento da excepção <tt>ClassNotFoundException</tt>, que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual.

Revision as of 12:47, 30 October 2013

Programação com Objectos
Introduction
Creation and Destruction
Inheritance & Composition
Abstraction & Polymorphism
Code Organization
Java Topics
Inner Classes
Enumerations
Data Structures
Exceptions
Input/Output
RTTI
Other Topics
JUnit Tests
UML Topics
Design Patterns
"Simple" Factory
Composite & Visitor
Command
Strategy & State
Template Method
Observer
Abstract Factory
Decorator & Adapter
Façade (aka Facade)

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 a execução de 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, 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: http://docs.oracle.com/javase/7/docs/api/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).


Exemplo

Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos.

Introdução

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: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 visao matematica: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.

Interfaces

Além dos aspectos acima, cada obra é classificada com uma ou mais interfaces, sendo o seu comportamento definido por elas.

 interface Folheável {
   void folhear();
 }
 interface Legível {
   void ler();
 }
 interface Rodopiável {
   void rodopiar();
 }

Estas interfaces não são realmente utilizadas para os aspectos de criação dos objectos, mas permitem ilustrar a utilização de código específico em conjunto com código genérico.

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).

<java5> 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 {
      ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1);
      Class tipo = Class.forName(dados[0]);
      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;
    }
  }
}

</java5>

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:

 public class DVD extends Obra implements Rodopiável, Legível {
   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:

 public class Livro extends Obra implements Folheável, Legível {
   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(); }
 }

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).

 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) throws IOException {
     String entrada = System.getProperty("obras", "obras.txt");
     BufferedReader r = new BufferedReader(new FileReader(entrada));
     String linha;
     ArrayList<Obra> obras = new ArrayList<Obra>();
     while ((linha = r.readLine()) != null) {
       Obra o = Obra.cria(linha);
       obras.add(o);
       System.out.println(o);
     }
     r.close();
     System.out.println("****************");
     for (Obra o: obras) o.processa();
   }
 }

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 visao matematica: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:Joao Fonseca:1234567890
 LER Livro:A arte de sobreviver no 36:Joao 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 visao matematica:Carlos Alberto Fonseca:1234567892
 LER Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892