Funções 'genéricas'

18 respostas
I

Não sei que nome tem isso (na verdade nao sei nem se eh java basico),mas tenho 4 arquivos pra ler,todos com o mesmo separador,mas qtde diferentes de campos.Isso eh uma função que só le o arquivo dos usuarios do sistema (falta a parte de criptografia ainda,mas isso vem depois):

public void leUsuarios(Vector <Usuario> V)
{	Scanner cin = null;
	try {
	   cin = new Scanner(ArqUsuarios);
	} catch (FileNotFoundException e) {
		System.err.println(e.getMessage());
	}
		
	String Str;
	String [] Partes = new String [3];

	while (cin.hasNext() == true)
	{	Str = cin.next();
		Partes = Str.split(separador);
		Usuario UTemp = new Usuario(Partes[0],Partes[1],Partes[2]);
		V.add(UTemp);
	}
}

Em vez de fazer as 4 funçoes,nao tem jeito de fazer um template (esse é o nome em C++: http://www.cplusplus.com/doc/tutorial/templates.html) que ‘acomode’ os Vector ?

Não sei como ficaria o new String [3],se posso colocar um método p/ setar a capacidade do array baseado no número de campos (talvez um final int passado como argumento – nao sei se eh gambiarra demais)

18 Respostas

ViniGodoy

Oi,

Os generics são diferentes dos templates, tanto na forma quanto são implementados, quanto na capacidade do uso. O java não criará uma classe diferente para cada generic encontrado, como o C++ fará.

Mas, respondendo a sua pergunta, teria sim como fazer uma função que acomode vários Vectors. Entretanto, você teria que fornecer uma implementação concreta na hora da criação de seu objeto. Penso em algo mais ou menos assim:

public <T> void leDados(File arquivo, List<T> list, Fabrica<T> fabrica) {
   Scanner cin = null;
   try {
      cin = new Scanner(arquivo);
   } catch (FileNotFoundException e) {
      throw new RuntimeException(e);
   }
 		
   while (cin.hasNext())
   {	
      String str = cin.next();
      T temp = fabrica.criar(str.split(separador));
      list.add(temp);
   }
}
E também seria necessária a interface:
public interface Fabrica<T> {
    T criar(String[] dados);
}
A implementação dessa interface para os usuários seria:
public class UsuarioFabrica implements Fabrica<Usuario> {
   public Usuario criar(String[] dados) {
      return new Usuario(dados[0],dados[1],dados[2]);
   }
}
Depois, um exemplo de uso de todo conjunto junto seria:
List<Usuario> listaDeusuarios = new ArrayList<Usuario>();
Fabrica<Usuario> fabricaDeUsuarios = new  UsuarioFabrica();
leDados(arqUsuarios, listaDeUsuarios, fabricaDeUsuarios);
ViniGodoy

Algumas dicas de java em geral:

Nomes de variáveis e propriedades iniciam com letras minusculas e tem palavras separadas por letras maiusculas: arqUsuario, fabricaDeUsuarios, etc.

No lugar do Vector, use a interface List e a classe ArrayList, como descrito. Há uma série de vantagens em adotar as classes novas da collections framework (o arraylist, por exemplo, não é sincronizado, enquanto o vector é).

I

que troço é esse Factory?
qual é a diferenca entre esse primeiro metodo e o interface? (é.sou bem crua mesmo. 3 anos de C/C++ fazem mal)

??? mas qual a vantagem de nao ser sincronizado?na doc online diz que caga tudo se tiver mais threads modificando.

Isso eh uma das coisas que eu acho um pé no saco em Java:classes diferentes que fazem quase a mesma coisa.E pra entender qd vc usa uma ou outra?

ViniGodoy

Para tornar a sua função mais genérica, é necessário que a criação não esteja dentro dela.

Então, criei uma interface chamada Factory para separar as coisas.

A factory me diz quantos campos o T em questão e, passado uma lista com esse mesmo número de Strings para ela, ela cria o T para mim. No caso do usuário, temos 3 campos e a forma de criar um novo usuário usando eles seria: new Usuario(campo[0], campo[1], campo[2]).

Se eu quisesse usar o método lê dados para outra classe completamente diferente, bastaria eu escrever uma nova factory.

T

Ponha o código abaixo em um arquivo TesteLe.java, compile-o e crie um arquivo “usuarios.txt” para testá-lo.

Por favor, em código novo, tente não usar a classe Vector; use List e ArrayList, conforme mostrado abaixo.

import java.io.*;
import java.util.*;
import java.lang.reflect.*;

class Usuario {
    private String nome, endereco, telefone;
    public Usuario (String... campos) {
        nome = campos[0]; endereco = campos[1]; telefone = campos[2];
    }
    public String toString() { return nome + "-" + endereco + "-" + telefone; }
}

class Pedido {
    public Pedido (String... campos) {
    }
}

class Cliente {
    public Cliente (String... campos) {
    }
}

class Estoque {
    public Estoque (String... campos) {
    }
}

class Le<T> {
    String separador = ",";
    Class<T> klass;
    
    public Le(Class<T> klass) {
        this.klass = klass;
    }
    
    public void le(List<T> lista, BufferedReader br) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        String linha;
        while ((linha = br.readLine()) != null) {
            String[] partes = linha.split (separador);
            // vamos pegar o construtor da classe T que aceita 1 parâmetro:
            // String[] campos (note que String[] campos e String... campos
            // são a mesma coisa para efeitos de achar o construtor)
            Constructor<T> cons = klass.getConstructor (String[].class);
            T t = cons.newInstance ((Object) partes);
            lista.add (t);
        }
    }
}

class TesteLe {
    public static void main(String[] args) throws Exception {
        Le<Usuario> leUsuarios = new Le<Usuario>(Usuario.class);
        BufferedReader br = new BufferedReader (new FileReader ("usuarios.txt"));
        List<Usuario> usuarios = new ArrayList<Usuario>();
        leUsuarios.le (usuarios, br);
        br.close();
        System.out.println (usuarios);
        Le<Pedido> lePedidos = new Le<Pedido> (Pedido.class);
        BufferedReader br2 = new BufferedReader (new FileReader ("pedidos.txt"));
        List<Pedido> pedidos = new ArrayList<Pedido>();
        lePedidos.le (pedidos, br2);
        br2.close();
        System.out.println (pedidos);
    }
}
I

Pq? (deve ter um motivo decente,pelo menos)

ViniGodoy

O Vector foi depreciado após o aparecimento da Collections Framework. Foi substituído pela classe ArrayList, mas ainda está lá por questões de compatibilidade.

Ele possui todos os métodos sincronizados, o que implica num custo de performance que normalmente você não precisaria ter. Ele também tem métodos fora do padrão da collections, como addElement, recaindo numa interface mais complicada.

Você também pode criar um ArrayList sincronizado, mas só se quiser. Normalmente a classe que usa o list é sincronizada, dispensando o próprio list em si de se-lo.

Via de regra, não use Vector e  preferência para as classes da collections como ArrayList ou LinkedList. O mesmo vale para a classe Hashtable, que foi substituida por HashMap ou TreeMap. Finalmente, use sempre esses classes através de sua interface. Ao invés de:

ArrayList list = new ArrayList();

Faça:

List<Usuario> list = new ArrayList<Usuario>();

Isso permite trocar a implementação do tipo de lista facilmente no futuro (de Array para Linked, ou para uma lista sincronizada), caso precise faze-lo. Por exemplo, se no futuro você precisasse sincronizar o list, bastaria alterar a lista para:
List<Usuario> list = new Collections.synchronizedList(new ArrayList<Usuario>());

T

De fato, o exemplo que dei poderia ser modificado para usar “factory”.

Uma dúvida que a (ou o ) Ísis teve é que você acha que é necessário dimensionar o array na sua declaração:

String [] Partes = new String [3];

Nesse caso em particular, você vai criar um array que logo será descartado, já que “String.split” dimensiona outro array de Strings com o tamanho correto. Nesse caso nem precisaria dimensionar o array, você poderia só declará-lo:

String[] partes;

ViniGodoy

Realmente, isso não parece necessário. Alterei o meu código ali em cima levando isso em consideração.

I
<blockquote>public interface Factory {

int numeroDeCampos();

T criar(String[] dados);

}</blockquote>

mas esse criar(String [] dados) tem a implementacao onde? no UsuarioFactory? Pq pelo que eu entendi a interface tem somente declaracoes.

ViniGodoy

Veja lá em cima a classe UsuarioFabrica, que fiz logo abaixo da definição da interface…

Seria uma possível implementação dessa interface. E deveria estar dentro do seu próprio arquivo .java.

I

blz.vou tentar aqui,mas sabem de algum material decente que nao seja altamente tecnico pra eu entender um pouco do basico disso?

ViniGodoy

Bom, é difícil que esse material não seja altamente técnico, pois o assunto é bem avançadinho…

Mas dê uma olhada no Generics Tutorial, da própria sun, que tenta deixar tudo um tanto digerível.

Quanto a interface de fábrica, procure sobre os padrões de projeto Factory Method e Abstract Factory. Você pode ler sobre isso no livro Padrões de Projeto, onde você também verá exemplos em C++.

T

Por exemplo: eu tive de escrever

Constructor<T> cons = klass.getConstructor (String[].class);
             T t = cons.newInstance ((Object) partes);

em vez de

T t = new T (partes);

que é algo que você poderia fazer em C++ (usando templates) ou em C# 2.0 (onde o “generics” é “reificado” - não me pergunte porque eles usam essa palavra latina).

Chato, não? Mas você só fica sabendo que isso é necessário depois de ler o tal tutorial, e se tiver mais dúvidas, consulte o site da Angelika Langer (nem que seja para olhar a foto da dona Angelika) , que é muito mais completo que o tal Tutorial:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

I

vou ler esse negocios (nem que seja pra ter uma ideia geral)* e ver se no javaranch tem algo a mais.

  • Nao falam que é na necessidade que se aprende?
I

noobice eh foda...
colei o codigo como vcs deixaram e tentei entende,nem q fosse por cima,mas...

>>SisMain
Arq.leDados(Arq.getArqUsuarios(), ListaDeUsuarios, fabricaDeUsuarios);
Arq.leDados(Arq.getArqClientes(), ListaDeClientes, fabricaDeClientes);

E me aparece um 'warning':

Multiple markers at this line -Type safety: Unchecked invocation leDados (File,ArrayList,Factory) of the generic method leDados(File,ArrayList,Factory) of type Arquivos - Type safety: The expression of type ArrayList needs unchecked conversion to conform to ArrayList
seguinte:dedsse jeito ele imprime certinho os campos da classe Usuario
Arq.leDados(Arq.getArqUsuarios(), ListaDeUsuarios, fabricaDeUsuarios);
	
		for (int i=0;i< ListaDeUsuarios.size();i++)
		{	Usuario T = (Usuario) ListaDeUsuarios.get(i);
			System.out.println(T.getLogin()+":"+T.getPermissao()+":"+T.getSenha());
		}
Mas assim:
Arq.leDados(Arq.getArqUsuarios(), ListaDeUsuarios, fabricaDeUsuarios);
		Arq.leDados(Arq.getArqClientes(), ListaDeClientes, fabricaDeClientes);
		
		for (int i=0;i< ListaDeUsuarios.size();i++)
		{	Usuario T = (Usuario) ListaDeUsuarios.get(i);
			System.out.println(T.getLogin()+":"+T.getPermissao()+":"+T.getSenha());
		}
		
		for (int i=0;i<ListaDeClientes.size();i++)
		{	Cliente C = (Cliente) ListaDeClientes.get(i);
			System.out.println(C.getDataCadastro()+":"+C.getEmail()+":"+C.getEndereco()+":"+C.getNome()+":"+C.getTelefone()+":"+C.getCpf());
			
		}

Aparece a mensagem

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at ClienteFactory.criar(ClienteFactory.java:7)
at ClienteFactory.criar(ClienteFactory.java:1)
at Arquivos.leDados(Arquivos.java:90)
at SisMain.main(SisMain.java:16)

Mas na linha 7 do ClienteFactory.java está tudo certo...6 campos.Eu perdi alguma coisa?
public Cliente criar(String[] dados) {
		return new Cliente(dados[0],dados[1],dados[2],dados[3],dados[4],dados[5]);
	}
ViniGodoy

O primeiro warning diz que alguma das suas classes deveria estar tipada, mas não está. Por exemplo, a lista de usuários, você criou assim:
List ListaDeUsuarios= new ArrayList()

Ou assim:
List<Usuario> ListaDeUsuarios = new ArrayList<Usuario>()
?

A segunda forma é a exigida pelo método.

Já, quanto ao segundo erro:
Aparentemente não há tantos campos assim no arquivo de clientes. Você tem certeza que o arquivo está correto? Talvez em alguma das linhas tenha apenas 5 campos…

I

Eu tinha feito do primeiro modo,mas coloquei do segundo ( List<Usuario> … ) e o 1o warning continua lá.

isis@DeadParrot:~&gt cat workspace/Ci067/db/clientes.dat
19325387[size=18];[/size]jasdjaks ansdiaosd[size=18];[/size]rua sd9082,894 cep:83690-279[size=18];[/size][email removido][b][size=18];[/size]/b876-3759[size=18];[/size]01/03/1984

public Cliente(String s1,String s2,String s3,String s4,String s5,String s6) {
		cpf = s1;
		nome = s2;
		endereco = s3;
		email = s4;
		telefone = s5;
		dataCadastro = s6;
	}

Tem 6 campos…Eu sei que ele nao tem problemas como os do strrtok ©,pq já que funcionou com o arquivo de usuarios…

Criado 5 de abril de 2007
Ultima resposta 6 de abr. de 2007
Respostas 18
Participantes 3