Sunday, March 29, 2015

ArrayLists e Serialização

Devido à um projeto externo, tive que trabalhar bastante com estruturas de dado e maneiras de armazená-los em formato de arquivo. Esse projeto envolvia o armazenamento e procura de hyperlinks (cada um deles com um nome, um comentário, uma metatag, uma data de criação e uma possível data de modificação).

Para facilitar a procura pelo database usando qualquer um desses critérios como parâmetro, a estrutura de dados necessariamente teria que ser algo simples de se percorrer e retornar os resultados positivos, por isso resolvi que um ArrayList era a melhor solução.

Um ArrayList funciona como é esperado: É uma sequência de objetos (no meu caso, hyperlink, uma estrutura formada por 5 strings, nome, comentário, etc), entre elas tendo um ponteiro apontando para o próximo objeto. O java já tem uma implementação boa, com diversas funções para manipular sua lista de dados, como Add, Insert e Remove.


No entanto, como se trata de uma database de hyperlinks, não se pode perder o conteúdo quando se fecha o programa. Se ainda estivesse trabalhando com C, seria necessário implementar uma lógica para armazenar toda a informação em plain-text em um arquivo de texto e por fim gerar uma ArrayList à partir de qualquer arquivo de texto dentro desses parâmetros que foram definidos.

Felizmente, existe em java um método chamado serialização. Ele basicamente faz uma cópia do estrutura serializada em arquivo binário, e permite a deserialização (conversão do arquivo binário em estrutura de dado) sem maiores complicações.


Se o assunto continuar pertinente, entrarei em detalhes sobre a implementação da serialização e funções mais avançadas de ArrayList no próximo post. Até lá!

Sunday, March 22, 2015

Superclasses e Subclasses

No último projeto nós trabalhos com JFrame e interfaces gráficas, bem como definição e chamada de funções. Nessa semana o foco foi em classes, subclasses e hereditariedade. Em termos simples, na linguagem java é possível criar objetos segundo uma classe, assim tendo todas as propriedades e funções inerentes a essa classe. As coisas começam a ficar interessantes quando se falam de subclasses e superclasses. Funciona assim: Subclasses necessariamente possuem uma superclasse, podendo herdar propriedades e funções da classe-pai. Dessa forma, é possível ter várias classes com propriedades diferentes, porém herdando uma ou mais atributos de uma superclasse em comum.

Para se mostrar a definição da subclasse, usa-se a sintaxe extends, e.g. “class Circle extends Shape”

O projeto dessa semana foca justamente nessa habilidade de herança. Foi construída uma superclasse Shape com os atributos aName e as funções getName e calculateArea (que, apenas em Shapes, retorna zero). Então fez-se as subclasses Circle, Square e Triangle, herdando todas as propriedades e funções de Shape, porém cada uma com os atributos específicos de sua forma geométrica, bem como uma redefinição da função calculateArea. Para isso, basta programar a função calculateArea dentro de cada subclasse, utilizando-se a extensão super.calculateArea caso queira-se chamar a função da superclasse Shape.

A redefinição foi feita para se poder chamar a função de todas as subclasses dentro de um único for, permitindo assim a fácil edição do funcionamento do programa. É desnecessário em uma aplicação tão simples, mas é análogo ao processo que permitiria a redefinição em programas muito maiores e mais complexos.

Segue agora o código completo, comentado.

1:  public class Shape {  
2:  private String name;  
3:  Shape(String aName) {name=aName;} //propriedade do nome, inerente à todas as subclasses  
4:  public String getName() {return name;}  
5:  public float calculateArea() {return 0.0f;} //percebe-se aqui o funcionamento da redefinição de funções nas subclasses  
6:  public static void main (String argv[])  
7:  {  
8:   Circle C = new Circle("Circle C"); //Cria os objetos  
9:   Square S = new Square("Square S");  
10:   Triangle T = new Triangle("Triangle T");  
11:   Shape shapeArray[] = {C,S, T};  
12:   //aqui pode-se rodar funções completamente diferentes sob um mesmo nome (calculateArea) pois cada uma é chamada enquanto dentro do escopo de uma subclasse específica  
13:   for (int i=0; i System.out.println("The area of " + shapeArray[i].getName() + " is " + shapeArray[i].calculateArea() + " sq. cm.\n");  
14:   }  
15:  }  
16:  }  
17:  class Circle extends Shape {  
18:   private int radius;  
19:   Circle(String aName){  
20:   super(aName); //Super é chamado para usar a propriedade do pai  
21:   radius = 3;  
22:   }  
23:   public float calculateArea(){  
24:   float area;  
25:   area = (float) (3.14*radius*radius);  
26:   return area;  
27:   }  
28:  }  
29:  class Square extends Shape {  
30:   private int side;  
31:   Square(String aName){  
32:   super(aName);  
33:   side = 3;  
34:   }  
35:   public float calculateArea(){  
36:   int area;  
37:   area = side*side;  
38:   return area;  
39:   }  
40:   }  
41:  class Triangle extends Shape {  
42:   private int side1, side2;  
43:   private double angle;  
44:   Triangle(String aName){  
45:   super(aName);  
46:   side1 = 2;  
47:   side2 = 3;  
48:   angle = 30.0;  
49:   }  
50:   public float calculateArea() {  
51:   float area;  
52:   area = (float)(0.5*side1*side2*Math.sin(Math.toRadians(angle)));  
53:   return area;  
54:   }  
55:  }  


Depois de executado, tem-se o output:

The area of Circle C is 28.26 sq. cm.

The area of Square S is 9.0 sq. cm.

The area of Triangle T is 1.5 sq. cm.

Até o próximo projeto!

Sunday, March 15, 2015

Continuação: Calculadora com interface gráfica!

Continuando com a atividade de anteontem, hoje implementei os métodos para as funções da calculadora, bem como o funcionamento dos botões. Alguns problemas ainda podem ser vistos por causa da transformação entre string e double da calculadora, por isso vou listar alguns detalhes que poderiam ser melhorados:
  • A função +/- inclui o sinal do negativo à direita do número. Poderia usar a excessão que usei na função getResult para resolver a estética, mas então seria impossível usar o número com as outras funções (pois a conversão de string para double supõe aquela posição do sinal negativo). Então esse detalhe foi ignorado para permitir a funcionalidade da calculadora.
  • Quando se aperta o botão ".", o visor mostra os números de forma errada. Ainda é um mistério para mim o porquê da chamada display.append("."); colocar o ponto antes dos números. Funcionalmente, no entanto, está tudo perfeito, e o resultado sempre é imprimido de maneira correta.
  • Contas com inteiros sempre contém casas decimais, por causa da conversão para double. Isso pode ser consertado facilmente, mas não achei necessário para o exercício.
Para que os leitores possam rodar o código com mais facilidade, postarei todo o projeto. As partes modificadas desde sexta-feira são dentro da função actionPerformed, mais todas as funções à partir da clear.

1:  package calculator;  
2:  import java.awt.*;  
3:  import javax.swing.*;  
4:  import java.awt.event.*;  
5:   //@author Gabriel  
6:  public class Calculator extends JFrame implements ActionListener{  
7:    //Declarando variáveis  
8:    JPanel[] row = new JPanel[5];  
9:    JButton[] button = new JButton[19];  
10:  //colocando todas as strings em um único vetor  
11:    String[] buttonString = {"7","8","9","+",   
12:       "4","5","6","-",   
13:       "1","2","3","*",  
14:       ".","/","C","sqrt",  
15:       "+/-","=","0"};  
16:    //Definindo tamanho do display e botões  
17:    Dimension displayDimension = new Dimension(300, 35);  
18:    Dimension regularDimension = new Dimension(45, 40);  
19:    Dimension rColumnDimension = new Dimension(60, 40);  
20:    Dimension zeroButtonDimension = new Dimension(90, 40);  
21:    boolean[] function = new boolean[4]; //vetor para armazenar a função pedida pelo usuário  
22:    double[] temporary = {0,0}; //variáveis auxiliares para calcular resultados  
23:    JTextArea display = new JTextArea(1,20);  
24:    boolean decimal = false; //para garantir que a função "." não possa ser inserida mais de uma vez  
25:    public static void main(String[] args)  
26:    {  
27:      Calculator c = new Calculator();   
28:    }  
29:    Calculator()  
30:    {  
31:      //o tutorial não explica a necessidade dessa função super. No entanto, procurando na internet, parece que  
32:      //ela é responsável por invocar o construtor pai  
33:      //super("Calculator");  
34:      setDesign();  
35:      setSize(380,250);  
36:      setResizable(false); //se o windows não deixa ajustar o tamanho da calculadora, por que eu iria?  
37:      setDefaultCloseOperation(EXIT_ON_CLOSE);  
38:      //Como vou querer 5 linhas e 5 colunas  
39:      GridLayout grid = new GridLayout(5,5);  
40:      setLayout(grid);  
41:      //inicializando o vetor de funções  
42:      for(int i=0;i<4 br="" i="">        function[i] = false;  
43:      FlowLayout f1 = new FlowLayout(FlowLayout.CENTER); //utilizamos a função FlowLayout para posicionar as colunas e linhas  
44:      FlowLayout f2 = new FlowLayout(FlowLayout.CENTER,1,1); //1,1 são os espaçamentos horizontais e verticais em relação a f1  
45:      for(int i=0; i<5 br="" i="">        row[i] = new JPanel();  
46:      row[0].setLayout(f1);  
47:      for(int i = 1; i<5 br="" i="">        row[i].setLayout(f2);  
48:      //agora as linhas e colunas já podem ser usadas!  
49:      for(int i = 0; i < 19; i++) //coloca as características dos botões  
50:      {  
51:        button[i] = new JButton();  
52:        button[i].setText(buttonString[i]);  
53:        button[i].addActionListener((ActionListener) this); //será usado para implementação do funcionamento do botão no futuro  
54:      }  
55:      display.setEditable(false); //não seria bom permitir o usuário editar o display  
56:      display.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //Por alguma razão desconhecida por mim, calculadoras geralmente tem o display escrevendo da direita para a esquerda  
57:      //definindo tamanhos!  
58:      display.setPreferredSize(displayDimension);  
59:      for(int i = 0; i < 14; i++)  
60:        button[i].setPreferredSize(regularDimension);  
61:      for(int i = 14; i < 18; i++)  
62:        button[i].setPreferredSize(rColumnDimension);  
63:      button[18].setPreferredSize(zeroButtonDimension);  
64:      //colocando o display e os botões nas linhas e colunas  
65:      row[0].add(display);  
66:      add(row[0]);  
67:      for(int i = 0; i < 4; i++)  
68:        row[1].add(button[i]);  
69:      row[1].add(button[14]);  
70:      add(row[1]);  
71:      for(int i = 4; i < 8; i++)  
72:        row[2].add(button[i]);  
73:      row[2].add(button[15]);  
74:      add(row[2]);  
75:      for(int i = 8; i < 12; i++)  
76:        row[3].add(button[i]);  
77:      row[3].add(button[16]);  
78:      add(row[3]);  
79:      row[4].add(button[18]);  
80:      for(int i = 12; i < 14; i++)  
81:        row[4].add(button[i]);  
82:      row[4].add(button[17]);  
83:      add(row[4]);  
84:      setVisible(true);  
85:    }  
86:    //necessário para possibilitar a compilação  
87:    public final void setDesign() {  
88:      try {  
89:        UIManager.setLookAndFeel(  
90:            "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");  
91:      } catch(Exception e) {   
92:      }  
93:    }  
94:   public void actionPerformed(ActionEvent ae)  
95:   {  
96:    if(ae.getSource() == button[0]) //7  
97:      display.append("7");  
98:    if(ae.getSource() == button[1]) //8  
99:      display.append("8");  
100:    if(ae.getSource() == button[2]) //9  
101:      display.append("9");  
102:    if(ae.getSource() == button[3]) //Soma  
103:    {  
104:      temporary[0] = Double.parseDouble(display.getText());  
105:      for(int i = 0; i < 4; i++) //Para evitar conflito quando multiplas teclas de função são apertadas  
106:        function[i] = false;  
107:      function[0] = true;  
108:      decimal = false;  
109:      display.setText("");  
110:    }  
111:    if(ae.getSource() == button[4]) //4  
112:      display.append("4");  
113:    if(ae.getSource() == button[5]) //5  
114:      display.append("5");  
115:    if(ae.getSource() == button[6]) //6  
116:      display.append("6");  
117:    if(ae.getSource() == button[7]) //Subtração  
118:    {  
119:      temporary[0] = Double.parseDouble(display.getText());  
120:      for(int i = 0; i < 4; i++)  
121:        function[i] = false;  
122:      function[1] = true;  
123:      decimal = false;  
124:      display.setText("");  
125:    }  
126:    if(ae.getSource() == button[8]) //1  
127:      display.append("1");  
128:    if(ae.getSource() == button[9]) //2  
129:      display.append("2");  
130:    if(ae.getSource() == button[10]) //3  
131:      display.append("3");  
132:    if(ae.getSource() == button[11]) //Multiplicação  
133:    {  
134:      temporary[0] = Double.parseDouble(display.getText());  
135:      for(int i = 0; i < 4; i++)  
136:        function[i] = false;  
137:      function[2] = true;  
138:      decimal = false;  
139:      display.setText("");  
140:    }  
141:    if(ae.getSource() == button[12]) //Decimal  
142:    {  
143:      if(!decimal)  
144:      {  
145:        decimal = true;  
146:        display.append(".");  
147:      }  
148:    }  
149:    if(ae.getSource() == button[13]) //Divisão  
150:    {  
151:      temporary[0] = Double.parseDouble(display.getText());  
152:      for(int i = 0; i < 4; i++)  
153:        function[i] = false;  
154:      function[3] = true;  
155:      decimal = false;  
156:      display.setText("");  
157:    }  
158:    if(ae.getSource() == button[14]) //C  
159:      clear();  
160:    if(ae.getSource() == button[15]) //Sqrt  
161:      getSqrt();  
162:    if(ae.getSource() == button[16]) //+/-  
163:      getPosNeg();  
164:    if(ae.getSource() == button[17]) //=  
165:      getResult();  
166:    if(ae.getSource() == button[18]) //0  
167:      display.append("0");  
168:  }  
169:    //Agora, para implementar o funcionamento dos botões, vou criar diversos métodos  
170:    //Para limpar a tela e resetar a calculadora  
171:    public void clear() {  
172:      try  
173:      {  
174:        display.setText(""); // Apaga o visor  
175:        for(int i = 0; i < 4; i++)  
176:          function[i] = false; // Reseta o estado de todas as funções  
177:        for(int i = 0; i < 2; i++)  
178:          temporary[i] = 0; // Apaga as variáveis auxiliares  
179:        decimal = false;  
180:      } catch(NullPointerException e) {   
181:      }  
182:    }  
183:    //Para calcular a raiz quadrada  
184:    public void getSqrt() {  
185:      try {  
186:        double value = Math.sqrt(Double.parseDouble(display.getText())); //Cria uma variável (double) e calcula a raiz do display usando a biblioteca math  
187:        display.setText(Double.toString(value)); //Modifica o valor do display  
188:      } catch(NumberFormatException e) {  
189:    }  
190:    }  
191:    //Para o botão +/-  
192:    public void getPosNeg() {  
193:      try {  
194:        double value = Double.parseDouble(display.getText()); //Cria a variável (por simplicidade)  
195:        value = value * (-1);//Não é necessário se preocupar com o zero por (-1)*0 = 0  
196:        display.setText(Double.toString(value));//Escreve o resultado  
197:      } catch(NumberFormatException e) {  
198:    }  
199:    }  
200:    public void getResult() {  
201:      double result = 0;  
202:      temporary[1] = Double.parseDouble(display.getText());  
203:      String temp0 = Double.toString(temporary[0]); //Duas variáveis necessárias para a conversão string -> double dos dois números  
204:      String temp1 = Double.toString(temporary[1]);  
205:      try {  
206:        if(temp0.contains("-")) { //Se o número é negativo  
207:          String[] temp00 = temp0.split("-", 2); //Divide a string em dois  
208:          temporary[0] = (Double.parseDouble(temp00[1]) * -1); //Transforma em double como negativo  
209:        }  
210:        if(temp1.contains("-")) { //Analogamente  
211:          String[] temp11 = temp1.split("-", 2);  
212:          temporary[1] = (Double.parseDouble(temp11[1]) * -1);  
213:        }  
214:      } catch(ArrayIndexOutOfBoundsException e) {  
215:      }  
216:      try {  
217:        if(function[2]) //Multiplicação  
218:          result = temporary[0] * temporary[1];  
219:        else if(function[3]) //Divisão  
220:          result = temporary[0] / temporary[1];  
221:        else if(function[0]) //Soma  
222:          result = temporary[0] + temporary[1];  
223:        else if(function[1]) //Subtração  
224:          result = temporary[0] - temporary[1];  
225:        if(result>=0) //Imprime resultado  
226:          display.setText(Double.toString(result));  
227:        else //evita um bug com o sinal negativo aparecendo depois do número  
228:        {  
229:          display.setText(Double.toString((-1)*result));  
230:          display.append("-");  
231:        }  
232:        for(int i = 0; i < 4; i++)  
233:          function[i] = false; //Reseta variáveis de função  
234:        decimal = false;  
235:      } catch(NumberFormatException e) {  
236:      }  
237:    }  
238:  }  


Cuidado apenas na hora de copiar e colar o código em um IDE, pois a formatação pode ter sido modificada pelo código html do blog. Vou incluir uma animação da calculadora funcionando para os menos interessados em programação.




Obrigado, até o próximo projeto!

Friday, March 13, 2015

Começando: Calculadora com interface gráfica!

Esse blog é um diário para recordar meu aprendizado de java na matéria de CES-22 no Instituto Tecnológico de Aeronáutica. Fique à vontade para acompanhar meu progresso!

Nosso professor, Edgar Yano, deu algumas aulas sobre a linguagem Java e pediu, como primeiro projeto a implementação de uma calculadora com interface gráfica. Conversando com meus colegas, foi me passado um tutorial exatamente para o pedido e, felizmente, por ter um conhecimento básico/intermediário de C#, foi possível entendê-lo quase completamente sem maiores problemas.

Segue o link do tutorial:

http://www.dreamincode.net/forums/topic/321933-creating-a-calculator-using-jframe/

Depois de seguí-lo parcialmente, cheguei à uma casca de calculadora. Visor e botões em lugares adequados, porém nada é funcional. Segue o código, comentado:

1:  import java.awt.*;  
2:  //importando bibliotecas import javax.swing.*;  
3:  import java.awt.event.*;  
4:  public class Calculator extends JFrame implements ActionListener  
5:  {  
6:   //Declarando variáveis  
7:   JPanel[] row = new JPanel[5];  
8:   JButton[] button = new JButton[19];  
9:   String[] buttonString = {"7","8","9","+", //colocando todas as strings em um único vetor para poder usar  
10:    "4","5","6","-", //um loop no futuro e facilmente adicionar o texto aos botões  
11:    "1","2","3","*",  
12:    ".","/","C","sqrt",  
13:    "+/-","=","0"};  
14:   //Definindo tamanho do display e botões  
15:   Dimension displayDimension = new Dimension(300, 35);  
16:   Dimension regularDimension = new Dimension(45, 40);  
17:   Dimension rColumnDimension = new Dimension(60, 40);  
18:   Dimension zeroButtonDimension = new Dimension(90, 40);  
19:   boolean[] function = new boolean[4]; //vetor para armazenar a função pedida pelo usuário  
20:   double[] temporary = {0,0}; //variáveis auxiliares para calcular resultados  
21:   JTextArea display = new JTextArea(1,20);  
22:   public static void main(String[] args)  
23:   {  
24:   Calculator c = new Calculator();   
25:   }  
26:   Calculator()  
27:   {  
28:   //o tutorial não explica a necessidade dessa função super. No entanto, procurando na internet, parece que  
29:   //ela é responsável por invocar o construtor pai  
30:   //super("Calculator");  
31:   setDesign();  
32:   setSize(380,250);  
33:   setResizable(false); //se o windows não deixa ajustar o tamanho da calculadora, por que eu iria?  
34:   setDefaultCloseOperation(EXIT_ON_CLOSE);  
35:   //Como vou querer 5 linhas e 5 colunas  
36:   GridLayout grid = new GridLayout(5,5);  
37:   setLayout(grid);  
38:   //inicializando o vetor de funções  
39:   for(int i=0;i<4;i++)  
40:   function[i] = false;  
41:   FlowLayout f1 = new FlowLayout(FlowLayout.CENTER); //utilizamos a função FlowLayout para posicionar as colunas e linhas  
42:   FlowLayout f2 = new FlowLayout(FlowLayout.CENTER,1,1); //1,1 são os espaçamentos horizontais e verticais em relação a f1  
43:   for(int i=0; i<5; i++)  
44:   row[i] = new JPanel();  
45:   row[0].setLayout(f1);  
46:   for(int i = 1; i<5;i++)  
47:   row[i].setLayout(f2);  
48:   //agora as linhas e colunas já podem ser usadas!  
49:   for(int i = 0; i < 19; i++) //coloca as características dos botões  
50:   {  
51:     button[i] = new JButton();  
52:     button[i].setText(buttonString[i]);  
53:     button[i].addActionListener((ActionListener) this); //será usado para implementação do funcionamento do botão no futuro  
54:   }  
55:   display.setEditable(false); //não seria bom permitir o usuário editar o display  
56:   display.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //Por alguma razão desconhecida por mim, calculadoras geralmente  
57:   //tem o display escrevendo da direita para a esquerda  
58:   //definindo tamanhos!  
59:   display.setPreferredSize(displayDimension);  
60:   for(int i = 0; i < 14; i++)  
61:     button[i].setPreferredSize(regularDimension);  
62:   for(int i = 14; i < 18; i++)  
63:     button[i].setPreferredSize(rColumnDimension);  
64:   button[18].setPreferredSize(zeroButtonDimension);  
65:   //colocando o display e os botões nas linhas e colunas  
66:   row[0].add(display);  
67:   add(row[0]);  
68:   for(int i = 0; i < 4; i++)  
69:     row[1].add(button[i]);  
70:   row[1].add(button[14]);  
71:   add(row[1]);  
72:   for(int i = 4; i < 8; i++)  
73:     row[2].add(button[i]);  
74:   row[2].add(button[15]);  
75:   add(row[2]);  
76:   for(int i = 8; i < 12; i++)  
77:     row[3].add(button[i]);  
78:   row[3].add(button[16]);  
79:   add(row[3]);  
80:   row[4].add(button[18]);  
81:   for(int i = 12; i < 14; i++)  
82:     row[4].add(button[i]);  
83:   row[4].add(button[17]);  
84:   add(row[4]);  
85:   setVisible(true);  
86:   }  
87:   //necessário para possibilitar a compilação  
88:   public final void setDesign() {  
89:     try {  
90:       UIManager.setLookAndFeel(  
91:           "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");  
92:     } catch(Exception e) {   
93:     }  
94:   }  
95:   public void actionPerformed(ActionEvent ae)  
96:   {  
97:   }  
98:  }  


O funcionamento de todos os botões será implementado ainda nesse fim de semana.