• Nenhum resultado encontrado

Coisas que você não sabia sobre Generics

N/A
N/A
Protected

Academic year: 2021

Share "Coisas que você não sabia sobre Generics"

Copied!
7
0
0

Texto

(1)

_generics

Eduardo Guerra | guerraem@gmail.com é desenvolvedor de frameworks, editor-chefe da revista MundoJ e pesquisador do Instituto Nacional de Pesquisas Espaciais (INPE). Foi professor do Instituto Tecnológico de Aeronáutica (ITA), onde concluiu sua graduação, mestrado e doutorado. Suas pesquisas se concentram nas áreas de design, arquitetura e teste de software. Possui diversas certifi cações da plataforma Java e experiência como arquiteto de software. Participa de projetos de frameworks open-source, como SwingBean, Esfi nge e ClassMock. É autor do livro “Design Patterns com Java: projeto orientado a objetos guiado por padrões” lançado pela editora Casa do Código. Ele acredita que um bom software se faz mais com criatividade do que com código e vive em busca de melhores formas para seu desenvolvimento. 

Veja neste artigo como tirar todo

proveito da utilização de tipos

genéricos na linguagem Java.

O

suporte a tipos genéricos foi adicionado na lin-guagem Java a partir do JDK 1.5. A princípio ela foi anunciada como uma funcionalidade “opcional“, que poderia ou não ser utilizada pelos desenvolve-dores. É verdade que todo código legado continuou compilando sem o uso de tipos genéricos, porém na minha opinião qualquer funcionalidade adicionada como parte da linguagem está longe de ser opcio-nal para os desenvolvedores. Acredito que na época a ideia era não causar alarde, porém, hoje, o conhe-cimento de tipos genéricos é essencial a qualquer programador Java.

O uso de tipos genéricos é muito conhecido pela sua utilização na API de coleções, que disponibiliza um conjunto de classes cujo uso é essencial a qual-quer desenvolvedor. Quando se cria uma coleção, seja ela uma lista (List) ou um conjunto (Set), nor-malmente se cria a coleção para armazenar alguma coisa, ou seja, algum tipo específi co de classe. É aí que entram os tipos genéricos, para dizer se aquela lista é uma lista de pessoas (List<Pessoa>) ou se o conjunto é um conjunto de produtos (Set<Produto>).

Indo na contramão de algumas linguagens di-nâmicas, onde a ideia é ter o menor número de de-fi nições possível para tornar o código mais simples de ser criado, os tipos genéricos adicionam certa

verbosidade na linguagem em troca de uma maior segurança de código. Por exemplo, antes dos tipos genéricos, qualquer objeto poderia ser adicionado em uma lista, e isso criava a possibilidade que se ten-tasse realizar o cast desse objeto para a classe errada, causando erros em tempo de execução. Com o uso de tipos genéricos, pelo fato do tipo da coleção ser explicitamente defi nido, esse tipo de erro na maioria dos casos é detectado em tempo de compilação.

A ideia deste artigo é mostrar algumas coisas so-bre os tipos genéricos que a maioria das pessoas não sabe. Você sabia, por exemplo, que em alguns casos você consegue recuperar o tipo genérico utilizando refl exão? Você sabia que usando tipos genéricos você pode defi nir o tipo da exceção que será lançada por um método através da classe que o invoca? Consegui despertar sua curiosidade? Então vamos começar o artigo com aquela revisada de tipos genéricos para aqueles que estão um pouco enferrujados e em se-guida vamos direto para a parte mais divertida.

Tipos Genéricos em Java

A melhor forma de entender um tipo genérico é como um parâmetro para um tipo. Muitas vezes você quer defi nir uma classe, mas quer que o retorno de certos métodos ou que certos parâmetros possam ser

Coisas que você

não sabia sobre

Generics

Coisas que você

não sabia sobre

(2)

O suporte a tipos genéricos é uma funcionalidade de linguagem

que muitas vezes é subutilizada pelos desenvolvedores. Eu

per-cebo que muitos acabam se limitando a utilizar classes com

ti-pos genéricos, mas não consideram inclui-los em suas próprias

classes. Este artigo mergulha nas questões mais avançadas e

curiosas dos tipos genéricos, mostrando como eles podem ser

utilizados de uma forma que você nunca tinha imaginado.

definidos por quem está instanciando um objeto da-quela classe. A lista é um ótimo exemplo, pois quem a instancia para armazenar um determinado tipo vai querer que o método de adição só aceite aquele tipo e que o método de recuperação retorne aquele tipo.

A Listagem 1 mostra um exemplo de como uma classe define um tipo genérico e como essa classe é instanciada. Repare que na definição da classe Gra-fo, o tipo genérico <E> é definido em sua declaração e utilizado na definição dos métodos, no lugar de parâmetros e/ou retornos. Dessa forma, quando um objeto instancia a classe e define um tipo genérico, é como se para aquela instância todos os lugares onde foi definido E fossem substituídos pelo tipo configu-rado.

Listagem 1

. Exemplo de uma classe que define um tipo genérico.

//Definição de classe com tipo genérico

publicclass Grafo<E> {

publicvoid adicionarNo(E no){ ... }

publicvoid adicionarLigacao(E noA, E noB) { ... } public List<E> recuperaVizinhos(E no) { ... } }

//Instanciando a classe definindo tipo genérico

Grafo<Cidade> grafo = new Grafo<Cidade>();

Outro caso de utilização de tipos genéricos é quando se deseja definir uma interface e quer que a classe que vai implementá-la possa definir o tipo de alguns retornos e parâmetros. Dessa forma, pode-se definir uma interface de propósito mais geral e inserir parâmetros de acordo com a classe que está a implementando. De forma alternativa, a classe pode “propagar“ o tipo genérico para sua definição, deixando assim para que ele seja definido no momento de instanciar os objetos, como mostrado na Listagem 1.

A Listagem 2 apresenta o exemplo da interfa-ce DAO<E> que define um tipo genérico e da classe ProdutoDAO que implementa essa interface fixando o tipo genérico que será utilizado. Dessa forma, na própria classe, os retornos e parâmetros definidos utilizando o tipo genérico na interface, serão fixados de acordo com o tipo definido. Dessa forma, quem instanciar a classe ProdutoDAO nem saberá que ela implementa uma interface com um tipo genérico.

Listagem 2.

Exemplo de uma interface que define um tipo genérico.

//Definição de interface com tipo genérico

publicinterface DAO<E> { publicvoid salvar(E obj); public E recuperar(int id); public List<E> listarTodos(); }

//Definindo uma classe que fixa o tipo genérico

publicclass ProdutoDAO implements DAO<Produto> { publicvoid salvar(Produto obj) { ... }

public Produto recuperar(int id) { ... } public List<Produto> listarTodos(){ ... } }

Além de poder ser definido em classes em interfaces, o tipo genérico também pode ser definido no contexto de um método. O principal objetivo desse uso é amarrar os tipos dos

parâmetros passados ou permitir que o retorno seja inferido de acordo com o parâmetro passado.

A Listagem 3 apresenta o exemplo de um mé-todo que define um tipo genérico para amarrar os parâmetros. Observe que ele recebe como parâmetro uma List<E> e um E, então a lista precisa ser do mes-mo tipo do elemento, senão será apontado um erro em tempo de compilação. Por exemplo, um código que passar um List<Integer> e uma String como pa-râmetro não será válido.

(3)

Listagem 3.

Exemplo de método que usa tipos gené-ricos para amarrar os parâmetros.

publicstatic <E> void colocarNaFrente(List<E> lista, E elemento) { ... }

A Listagem 4 apresenta outro exemplo, onde o tipo genérico é utilizado para que o tipo do retorno seja inferido de acordo com o parâmetro passado. Por exemplo, se for passada uma lista de String, então o retorno será do tipo String. Isso evita que casts des-necessários sejam feitos e evita erros como a atribui-ção do retorno para uma variável de tipo incompatí-vel com o objeto retornado.

Listagem 4.

Exemplo de método que usa tipos gené-ricos para inferência de retorno.

//Definição do método

publicstatic <E> E elementoDoMeio(List<E> lista) { ... }

//Uso do método

List<String> lista = //recupera lista

String meio = elementoDoMeio(lista)

De forma bem rápida e resumida essa pequena in-trodução mostrou um pouco do funcionamento dos tipos genéricos. As próximas seções irão focar em coisas interessantes que podem ser feitas com tipos genéricos.

Restringindo Tipos Genéricos

Uma coisa que poucos sabem sobre os tipos gené-ricos é que é possível restringir quais tipos podem ser passados como parâmetro. Para ilustrar essa questão considere um método que grave em um arquivo uma lista de objetos. Como os objetos precisarão ser seria-lizados, não faz sentido receber uma lista em que os objetos não implementem Serializable. A Listagem 5 mostra como esse método poderia ser definido.

Listagem 5.

Exemplo de método que restringe o tipo genérico do parâmetro.

publicvoid gravarEmArquivo(List<? Extends

Serializable> lista) { ... }

Quando a restrição é utilizada em parâmetros de mé-todos, o “?“, chamado de wildcard, pode ser utilizado para definir um tipo que obedece certa regra de tipo. Se o wildcard for acompanhado de “extends“ significa que ele pode ser qualquer classe que estenda a classe ou implemente a interface. Se ele for acompanhado de “super“ significa que pode ser qualquer supertipo do configurado. Em ambos os casos a própria classe

ou interface é uma escolha válida.

Outro fato importante no exemplo da Listagem 5 é que, nesse caso, como o parâmetro não tem um tipo genérico definido, nenhum método que recebe o tipo genérico como parâmetro pode ser invocado, a não ser passando “null“. De forma mais concreta, o método add(), que recebe o tipo genérico da lista, não poderia ser invocado nesse caso. Sendo assim, seria como se a lista fosse read-only.

Outro caso em que isso pode ser muito útil é para restringir o tipo de classe que pode ser configurada em uma anotação. Por mais que possa parecer estra-nho, a classe Class possui um tipo genérico que é o tipo da própria classe que está representando. Sendo assim, a classe String retorna no método getClass() uma instância de Class<String>. Sendo assim, para tanto um método ou uma anotação que recebe uma classe como parâmetro, é possível definir apenas um subconjunto de classes que podem ser passadas como parâmetro.

Imagine, por exemplo, uma anotação chamada @RelatedDAO que configura qual a classe DAO que precisa ser utilizada para uma determinada entida-de. Devido a própria descrição, só faz sentido a con-figuração de classes que implementem uma interface chamada DAO. A Listagem 6 mostra como seria essa anotação.

Listagem 6.

Anotação que restringe a Class configu-rada em uma propriedade.

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public@interface RelatedDAO { Class<? extends DAO> value(); }

Para finalizar essa parte de restrição de tipos, uma classe também pode restringir os tipos genéricos que podem ser utilizados para parametrizá-la. Imagine que ao invés de criar um método para guardar uma lista em um arquivo, eu decida criar uma lista seriali-zável. Observe na Listagem 7 que é possível, mesmo implementando outra interface, restringir na decla-ração do tipo genérico qual classe ou interface ele precisa ter como supertipo. Nesse caso, ao invés do wildcard “?“, utiliza-se o próprio parâmetro genérico.

Listagem 7.

Exemplo de método que usa tipos gené-ricos para inferência de retorno.

publicclass SerializableList<E extends Serializable>

implements List<E> { ... }

(4)

você define que um tipo genérico deve implementar uma interface, então é possível invocar os métodos daquela interface mesmo que o tipo não esteja defi-nido. Esse tipo de definição também é útil para evitar que erros de tipagem ocorram na definição dos tipos genéricos.

Ok! Eu sei que essa seção não impressionou tanto quem conhecia um pouco mais sobre tipos genéricos! Mas prepare-se que na próxima seção as coisas ficam mais interessantes!

Definindo o Tipo da Exceção pelo

Cliente

Qualquer desenvolvedor Java aprende ao lidar com exceções que um método pode declarar as exce-ções que podem ser lançadas durante a sua execução. Mas poderia um cliente definir o tipo da exceção que será lançada por um método que ele invoca? Surpre-endentemente utilizando tipos genéricos sim!

Muitas vezes acabamos capturando a exceção lançada por um componente para a encapsularmos dentro de uma exceção definida pela aplicação. Nesse cenário seria muito interessante que fosse possível definir para esse componente qual exceção ele deve lançar, pois dessa forma não seria preciso mapear as suas exceções para as da aplicação.

O exemplo dessa seção considera uma classe que autentica usuários a partir de definições em um ar-quivo de propriedades com login e senha. Um dos re-quisitos é que esse componente lance uma exceção toda vez que um usuário não for autenticado. Para permitir seu reúso, deseja-se então que o cliente pos-sa definir a classe da exceção que será lançada.

O segredo dessa solução está na definição de uma interface com tipo genérico para representar uma fá-brica de exceções, como a classe Fafá-bricaExcecoes na Listagem 8. O tipo genérico dessa interface represen-ta a exceção que é criada por ela. A intenção é que as classes ao implementarem essa interface definam o tipo da exceção que irão criar.

Listagem 8.

Interface com tipo genérica para criação de exceções.

publicinterface FabricaExcecoes<E extends Exception> { public E criarExcecao(String msg);

}

A Listagem 9 apresenta a classe Autenticador, a qual é responsável pela autenticação dos usuários. No construtor é feito o carregamento do arquivo de propriedades “users.prop” onde os usuários e suas senhas estão armazenadas (a ideia aqui é apenas ilustrar a técnica, não armazene senhas assim “em casa“). O método autenticar() define um tipo gené-rico E, que é utilizado para um parâmetro do tipo FabricaExcecoes<E> e para o tipo de exceção lançada

por ele. Dessa forma, o tipo da exceção lançada irá de-pender do tipo genérico da fábrica, a qual é definida pelo cliente. Observe que a exceção lançada é criada pela fábrica.

Listagem 9.

Classe que faz a autenticação de um usuário e declara uma exceção genérica.

publicclass Autenticador { private Properties p; public Autenticador(){ try { p = new Properties();

p.load(new FileInputStream(“users.prop”)); } catch (Exception e) {

thrownew RuntimeException(“Usuarios não encontrados”);

} }

public <E extends Exception> void

autenticar(String login, String senha, FabricaExcecoes<E> f) throws E {

Cuidado! Os tipos genéricos são invariantes!

Uma coisa que confunde muitas pessoas que co-meçam a trabalhar com tipos genéricos é como funciona a herança e o polimorfismo. Os arrays são tipos covariantes, isso significa que um ar-ray de um tipo A é considerado por polimorfismo um array do tipo B, se B for um supertipo de A. Por exemplo, se eu tenho uma variável do tipo Integer[] então ela pode ser atribuída para uma do tipo Number[] via polimorfismo.

Com os tipos genéricos as regras são diferentes, pois eles são invariantes. Nesse caso, uma classe nunca pode ser convertida por polimorfismo para uma classe com um tipo diferente. Isso significa que uma variável do tipo List<Integer> não pode ser atribuída para uma do tipo List<Number>. Se você quiser receber variáveis com mais de um tipo genérico, é preciso utilizar o wildcard com as palavras super e extends de acordo com a res-trição desejada.

No caso de métodos que utilizarem um wildcard com extends em um parâmetro, existe a restri-ção de não ser possível invocar nessa instância métodos que recebem o tipo genérico. De forma equivalente, quando o wildcard com super for utilizado, não pode ser feita a inferência do tipo do retorno, que deverá ser sempre recebido como Object.

(5)

if(!(p.contains(login) && p.get(login).equals(senha))){

throw f.criarExcecao(“Login e senha incorretos!”); }

} }

Para entender melhor como isso funciona, será mostrado um exemplo concreto. A Listagem 10 apre-senta a exceção SecurityException, que no exemplo faz o papel de uma exceção que foi definida para a aplicação. Já na Listagem 11, a classe FabricaSecuri-tyException implementa a interface FabricaExcecoes com o tipo genérico igual a exceção definida, no caso SecurityException.

Listagem 10.

Exceção definida para ser lançada pelo método.

publicclass SecurityException extends Exception {

public SecurityException(String message) { super(message);

} }

Listagem 11.

Implementação da fábrica de exceções para a exceção definida.

publicclass FabricaSecurityException implements

FabricaExcecoes<SecurityException> {

@Override

public SecurityException criarExcecao(String msg) { returnnew SecurityException(msg);

} }

A Listagem 12 apresenta um código cliente que utili-za a classe Autenticador. Observe que uma instância da classe FabricaSecurityException é criada para que o tipo da exceção possa ser definido. Quando essa classe é passada como parâmetro, o tipo da exceção é inferido pelo tipo genérico da fábrica. Sendo assim, observe que o bloco try/catch captura a exceção que é definida pela aplicação.

Listagem 12

. Código que invoca a classe Autentica-dor definindo a exceção que ela irá lançar.

publicclass Principal {

publicstaticvoid main(String[] args) { Autenticador a = new Autenticador(); FabricaSecurityException f = new FabricaSecurityException(); try {

a.autenticar(“admin”, “admin”, f);

System.out.println(“Autenticação com sucesso”); } catch (SecurityException e) {

System.out.println(e.getMessage()); }

} }

Outra abordagem nessa solução seria a utilização de um tipo genérico de classe ao invés de um tipo gené-rico de método. Dessa forma, a fábrica de exceções precisa ser configurada apenas uma vez no construtor e não precisa ser passada todas as vezes como parâ-metro. Da outra forma, a mesma instância poderia lançar diferentes exceções de acordo com o parâme-tro do método, e nessa solução a exceção é definida no momento da criação. A Listagem 13 mostra como ficaria essa implementação.

Listagem 13.

Utilizando tipo genérico de classe no Autenticador.

publicclass Autenticador<E extends Exception> { private Properties p; private FabricaExcecoes<E> f; public Autenticador(FabricaExcecoes<E> f){ this.f = f; try { p = new Properties();

p.load(new FileInputStream(“users.prop”)); } catch (Exception e) {

thrownew RuntimeException(“Usuarios não encontrados”); }

}

publicvoid autenticar(String login, String senha) throws

E {

if(!(p.contains(login) && p.get(login).equals(senha))){

throw f.criarExcecao(“Login e senha incorretos!”); }

} }

Recuperando o Tipo Genérico com

Reflexão

Uma coisa que sempre é comentada sobre os tipos genéricos, é que é são validações realizadas em tempo de compilação e que essa informação não é mantida em tempo de execução. Sendo assim, imagino que tal-vez você esteja se perguntando: como um tipo genérico pode ser recuperado por reflexão se ele não é mantido pela máquina virtual? Calma! Dado um objeto, não é possível saber o tipo genérico desse objeto em tempo de execução. Porém, em declarações de métodos, atri-butos ou de classe que utilizam tipos genéricos, é sim possível saber qual o tipo genérico declarado.

(6)

Considere, por exemplo, a classe Pessoa apresen-tada na Listagem 14. Imagine que fosse necessário criar um algoritmo que recuperasse todas as entida-des relacionadas com uma determinada classe. Nesse contexto, uma entidade seria toda classe com a ano-tação @Entity. Nesse caso, seria necessário varrer os atributos dessa classe e procurar pelos tipos que possuem essa anotação. Porém, por exemplo, o atri-buto telefones possui uma entidade declarada como um tipo genérico. Como fazer para buscar essa infor-mação?

Listagem 14

. Classe que possui tipos relacionados declarados em tipos genéricos.

@Entity

publicclass Pessoa {

private String nome; privateint idade;

private Endereco endereco; private List<Telefone> telefones; private Set<String> cargos;

//getters e setters omitidos

}

Na API de reflexão, a classe Field possui um mé-todo chamado getGenericType(), que não retorna os parâmetros de tipo, mas uma instância de Paramete-rizedType (apesar do retorno ser do tipo Type e ser necessário fazer o cast). Essa classe possui um méto-do chamaméto-do getActualTypeArguments() que retorna um array com os parâmetros genéricos configurados. A Listagem 15 mostra o método getEntidadesRe-lacionadas() que recebe uma classe e retorna uma lis-ta com as entidades relacionadas a ela. Primeiramen-te esse método verifica se a classe do atributo possui a anotação @Entity e, em caso positivo, a adiciona a lista que será retornada. Em caso negativo, é verifica-do se o tipo implementa a interface Collection e, em caso positivo, é recuperado o tipo genérico e no qual também é verificado se possui a anotação @Entity.

Listagem 15.

Método que retorna as entidades rela-cionadas, procurando nos tipos genéricos.

publicclass Relacoes { publicstatic List<Class>

getEntidadesRelacionadas(Class c){ List<Class> list = new ArrayList<>(); for(Field f : c.getDeclaredFields()){ Class tipo = f.getType();

if(tipo.isAnnotationPresent(Entity.class)){ list.add(tipo);

}elseif(Collection.class.isAssignableFrom(tipo)){

ParameterizedType tipoGenerico =

(ParameterizedType) f.getGenericType(); Class parametroGenerico = (Class)

tipoGenerico.getActualTypeArguments()[0]; if(parametroGenerico.isAnnotationPresent( Entity.class)){ list.add(parametroGenerico); } } } return list; } }

A Listagem 16 apresenta um exemplo de como esse método seria utilizado para retornar as entidades re-lacionadas na classe Pessoa e imprimir no console. No caso, será impresso o nome das classes Telefone e Endereco que possuem a anotação.

Listagem 16.

Classe que possui tipos relacionados declarados em tipos genéricos.

publicclass PrincipalReflection {

publicstaticvoid main(String[] args) { List<Class> lista =

Relacoes.getEntidadesRelacionadas(Pessoa.class); for(Class c : lista){

System.out.println(c.getName()); }

} }

Outro cenário onde a recuperação do parâmetro ge-nérico por reflexão pode ser muito útil é em relação ao tipo utilizado na superclasse. Imagine o exemplo de uma abstração para a representação de um DAO, como anteriormente apresentado na Listagem 2 des-te artigo. Muitas vezes, é preciso saber qual é a classe que será persistida para implementação dos métodos, principalmente se a implementação for JPA. Dessa forma, a superclasse pode disponibilizar um método para a recuperação do parâmetro genérico utilizado na subclasse.

A Listagem 17 apresenta como esse método pode ser implementado. A partir da classe do objeto, é feita uma busca nas superclasses até que seja encontrada a superclasse DAO. Então, o método getGenericSuper-class() é utilizado para recuperar uma instância de ParameterizedType e a partir dele serem recuperados os parâmetros genéricos.

Listagem 17.

Exemplo de uma superclasse com tipo genérico que fornece método para recuperação do parâmetro genérico configurado.

(7)

publicabstractvoid salvar(E obj); publicabstract E recuperar(int id); publicabstract List<E> listarTodos();

public Class getGenericParameter(){ Class c = this.getClass();

while(!c.getSuperclass().equals(DAO.class)){ c = c.getSuperclass(); } ParameterizedType tipoGenerico = (ParameterizedType) c.getGenericSuperclass(); return (Class) tipoGenerico.getActualTypeArguments()[0]; } }

Para completar o exemplo, a Listagem 18 mostra a classe DAOPessoa que estende a classe DAO com o tipo genérico Pessoa. O método main() cria uma ins-tância classe e invoca o método getGenericParame-ter() criado. Com a execução desse código, o nome da classe Pessoa será impresso no console.

Listagem 18.

Classe que estende a superclasse ge-nérica e retorna a classe do parâmetro genérico.

publicclass PessoaDAO extends DAO<Pessoa> {

publicstaticvoid main(String[] args) { PessoaDAO dao = new PessoaDAO();

System.out.println(dao.getGenericParameter(). getName());

}

//implementações dos métodos abstratos omitidas

}

Considerações finais

Este artigo teve como objetivo fazer uma revisão dos tipos genéricos e apresentar algumas utilizações desse recurso que são pouco exploradas pelos de-senvolvedores. Dentre as técnicas apresentadas está a restrição no parâmetro genérico de uma classe ou método, utilização de exceções genéricas e a recupe-ração do tipo genérico em declarações, como de atri-butos, de métodos e de classe.

Além de ser interessante e curioso a utilização de um recurso de linguagem pouco conhecido, ou mes-mo de uma forma que foge do convencional, este ar-tigo também discutiu os cenários em que cada uma dessas técnicas poderiam ser utilizadas. Através dos exemplos, foram mostrados cenários cotidianos de aplicações que ilustraram o contexto para o seu uso.

Design Patterns com Java:

projeto orientado a objetos guiado

por padrões

Se você se interessou por este artigo e gosta de estudar sobre técnicas de modelagem de software, acabou de ser lançado pela editora Casa do Código o livro “Design Patterns com Java: Projeto orientado a objetos guiado por padrões”. Esse livro apresenta, de uma forma didática e moderna, como utilizar os padrões de projeto para um bom design orientado a objetos em Java. Além dos padrões em si, o li-vro traz exemplos de APIs e frameworks Java que os utilizam. Adicionalmente, também são abordadas técnicas de design modernas, como o uso de interface fluente, componentes plu-gáveis e criação de frameworks. Dentro do contexto deste artigo, é abordado no último capítulo como os tipos genéricos podem ser utilizados na implementação dos padrões.

Referências

Documentos relacionados

Desta forma, este estudo tem como objetivo relatar a experiência vivenciada, por acadêmicas do 5º período do Curso de Enfermagem da Universidade Federal de

De um modo geral, as professoras demonstraram interesse em trabalhar com a deficiência em sala de aula e buscam meios para estarem incluindo os alunos com necessidade especial,

As relações de consumo entre instituições de ensino e a família terão um novo olhar depois da pandemia e mais uma vez a desigualdade social ficou escancarada através da urgência

18.1 O(s) produto(s) deverá(ao) ser entregue(s) rigorosamente dentro das especificações estabelecidas neste Edital e seus Anexos, sendo que a inobservância desta condição

BASES DO CONCURSO DE CONSULTORIA TÉCNICA A EMPRESAS / EMPRESÁRIOS / CRIAÇÃO E ADAPTAÇÃO DE MODELOS DE NEGÓCIOS BASEADOS EM ECODESIGN... DESIGN &amp; GREEN ENGINEERING

É com imenso prazer que a Confederação Nacional de Municípios (CNM), o Congresso de Intendentes do Uruguai e o Comitê Binacional de Fronteira de Intendentes e Prefeitos apresentam

• Nunca mergulhe o Termômetro Clínico Digital Sem Contato G-Tech Modelo FR1DZ1 em água ou em qualquer outro líquido (este aparelho não é à prova de água).. • NUNCA

ORAL/ESCRITO - FLEXIBILIDADE DE HORÁRIO O VALOR DO SALÁRIO REFERIDO NA OFERTA DE EMPREGO DEVE SER CONSIDERADO COMO PONTO DE REFERÊNCIA DADO QUE O MESMO SERÁ NEGOCIADO ENTRE