Pergunta

Pensei em oferecer esta bola de softball a quem quisesse rebatê-la fora do parque.O que são genéricos, quais são as vantagens dos genéricos, porquê, onde, como devo utilizá-los?Por favor, mantenha-o bastante básico.Obrigado.

Foi útil?

Solução

  • Permite que você escreva código/use métodos de biblioteca com segurança de tipo, ou seja,a List<string> é garantido como uma lista de strings.
  • Como resultado do uso de genéricos, o compilador pode realizar verificações em tempo de compilação no código para segurança de tipo, ou seja,você está tentando colocar um int nessa lista de strings?Usar um ArrayList causaria um erro de tempo de execução menos transparente.
  • Mais rápido do que usar objetos, pois evita boxing/unboxing (onde .net precisa converter tipos de valor para tipos de referência ou vice-versa) ou conversão de objetos para o tipo de referência necessário.
  • Permite escrever código aplicável a muitos tipos com o mesmo comportamento subjacente, ou seja,um Dictionary<string, int> usa o mesmo código subjacente que um Dictionary<DateTime, double>;usando genéricos, a equipe do framework só precisou escrever um trecho de código para obter os dois resultados com as vantagens mencionadas também.

Outras dicas

Eu realmente odeio me repetir.Odeio digitar a mesma coisa com mais frequência do que o necessário.Não gosto de repetir as coisas várias vezes com pequenas diferenças.

Em vez de criar:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

Posso construir uma classe reutilizável ...(no caso de você não querer usar a coleção bruta por algum motivo)

class MyList<T> {
   T get(int index) { ... }
}

Agora sou 3x mais eficiente e só preciso manter uma cópia.Por que você NÃO deseja manter menos código?

Isso também é verdadeiro para classes que não são de coleção, como Callable<T> ou um Reference<T> que tem que interagir com outras classes.Você realmente quer estender Callable<T> e Future<T> e todas as outras classes associadas para criar versões com segurança de tipo?

Eu não.

Não precisar de typecast é uma das maiores vantagens dos genéricos Java, pois realizará a verificação de tipo em tempo de compilação.Isto reduzirá a possibilidade de ClassCastExceptions que podem ser lançados em tempo de execução e podem levar a um código mais robusto.

Mas suspeito que você esteja plenamente consciente disso.

Toda vez que olho para os genéricos, isso me dá dor de cabeça.Acho que a melhor parte do Java é a simplicidade e a sintaxe mínima e os genéricos não são simples e adicionam uma quantidade significativa de nova sintaxe.

No início, também não vi o benefício dos genéricos.Comecei a aprender Java a partir da sintaxe 1.4 (embora o Java 5 já estivesse disponível na época) e quando encontrei genéricos, senti que era mais código para escrever e realmente não entendi os benefícios.

IDEs modernos facilitam a escrita de código com genéricos.

A maioria dos IDEs modernos e decentes são inteligentes o suficiente para ajudar na escrita de código com genéricos, especialmente na conclusão do código.

Aqui está um exemplo de como fazer um Map<String, Integer> com um HashMap.O código que eu teria que digitar é:

Map<String, Integer> m = new HashMap<String, Integer>();

E, de fato, isso é muito para digitar apenas para fazer um novo HashMap.No entanto, na realidade, eu só tive que digitar isso antes que o Eclipse soubesse o que eu precisava:

Map<String, Integer> m = new Ha Ctrl+Espaço

É verdade, eu precisava selecionar HashMap de uma lista de candidatos, mas basicamente o IDE sabia o que adicionar, incluindo os tipos genéricos.Com as ferramentas certas, usar genéricos não é tão ruim.

Além disso, como os tipos são conhecidos, ao recuperar elementos da coleção genérica, o IDE agirá como se aquele objeto já fosse um objeto do seu tipo declarado - não há necessidade de fazer casting para o IDE saber qual é o tipo do objeto. é.

Uma vantagem importante dos genéricos vem da maneira como eles funcionam bem com os novos recursos do Java 5. Aqui está um exemplo de lançamento de números inteiros em um Set e calculando seu total:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

Nesse trecho de código, há três novos recursos do Java 5 presentes:

Primeiro, genéricos e autoboxing de primitivos permitem as seguintes linhas:

set.add(10);
set.add(42);

O número inteiro 10 é autoboxed em um Integer com o valor de 10.(E o mesmo para 42).Então isso Integer é jogado no Set que é conhecido por conter IntegerS.Tentando lançar um String causaria um erro de compilação.

Em seguida, for-each loop leva todos os três:

for (int i : set) {
  total += i;
}

Primeiro, o Set contendo Integers são usados ​​em um loop for-each.Cada elemento é declarado como um int e isso é permitido como o Integer é desembalado de volta ao primitivo int.E o fato de esse unboxing ocorrer é conhecido porque genéricos foram usados ​​para especificar que havia Integeré realizado no Set.

Os genéricos podem ser a cola que reúne os novos recursos introduzidos no Java 5 e apenas torna a codificação mais simples e segura.E na maioria das vezes os IDEs são inteligentes o suficiente para ajudá-lo com boas sugestões, então, geralmente, não será necessário digitar muito mais.

E, francamente, como pode ser visto no Set Por exemplo, sinto que a utilização dos recursos do Java 5 pode tornar o código mais conciso e robusto.

Editar - Um exemplo sem genéricos

O seguinte é uma ilustração do acima Set exemplo, sem o uso de genéricos.É possível, mas não é exatamente agradável:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(Observação:O código acima irá gerar um aviso de conversão não verificada em tempo de compilação.)

Ao usar coleções não genéricas, os tipos inseridos na coleção são objetos do tipo Object.Portanto, neste exemplo, um Object é o que está sendo addinserido no conjunto.

set.add(10);
set.add(42);

Nas linhas acima, o autoboxing está em jogo - o primitivo int valor 10 e 42 estão sendo autoboxed em Integer objetos, que estão sendo adicionados ao Set.No entanto, tenha em mente que Integer objetos estão sendo tratados como Objects, já que não há informações de tipo para ajudar o compilador a saber qual tipo o Set deveria esperar.

for (Object o : set) {

Esta é a parte crucial.A razão pela qual o loop for-each funciona é porque o Set implementa o Iterable interface, que retorna um Iterator com informações de tipo, se houver.(Iterator<T>, aquilo é.)

No entanto, como não há informações de tipo, o Set retornará um Iterator que retornará os valores no Set como Objects, e é por isso que o elemento que está sendo recuperado no loop for-each deve ser do tipo Object.

Agora que o Object é recuperado do Set, ele precisa ser convertido em um Integer manualmente para realizar a adição:

  total += (Integer)o;

Aqui, um typecast é realizado a partir de um Object para um Integer.Nesse caso, sabemos que isso sempre funcionará, mas a conversão manual sempre me faz sentir que é um código frágil que pode ser danificado se uma pequena alteração for feita em outro lugar.(Eu sinto que cada typecast é um ClassCastException esperando para acontecer, mas estou divagando...)

O Integer agora está desembalado em um int e autorizado a realizar a adição no int variável total.

Espero poder ilustrar que os novos recursos do Java 5 podem ser usados ​​com código não genérico, mas não são tão limpos e diretos quanto escrever código com genéricos.E, na minha opinião, para aproveitar ao máximo os novos recursos do Java 5, deve-se olhar para os genéricos, se pelo menos permitir verificações em tempo de compilação para evitar que typecasts inválidos lancem exceções em tempo de execução.

Se você pesquisasse o banco de dados de bugs do Java pouco antes do lançamento do 1.5, encontraria sete vezes mais bugs com NullPointerException que ClassCastException.Portanto, não parece ser um ótimo recurso encontrar bugs, ou pelo menos bugs que persistem após alguns testes de fumaça.

Para mim, a grande vantagem dos genéricos é que documentam em código informações importantes sobre o tipo.Se eu não quisesse que essas informações de tipo fossem documentadas no código, usaria uma linguagem de tipo dinâmico ou pelo menos uma linguagem com mais inferência de tipo implícita.

Manter as coleções de um objeto para si mesmo não é um estilo ruim (mas o estilo comum é ignorar efetivamente o encapsulamento).Em vez disso, depende do que você está fazendo.Passar coleções para "algoritmos" é um pouco mais fácil de verificar (em ou antes do tempo de compilação) com genéricos.

Genéricos em Java facilitam polimorfismo paramétrico.Por meio de parâmetros de tipo, você pode passar argumentos para tipos.Assim como um método como String foo(String s) modela algum comportamento, não apenas para uma string específica, mas para qualquer string s, então um tipo como List<T> modela algum comportamento, não apenas para um tipo específico, mas para qualquer tipo. List<T> diz que para qualquer tipo T, há um tipo de List cujos elementos são Té.Então List é na verdade um construtor de tipo.Ele pega um tipo como argumento e constrói outro tipo como resultado.

Aqui estão alguns exemplos de tipos genéricos que uso todos os dias.Primeiro, uma interface genérica muito útil:

public interface F<A, B> {
  public B f(A a);
}

Esta interface diz que para alguns dois tipos, A e B, há uma função (chamada f) que leva um A e retorna um B. Ao implementar esta interface, A e B pode ser qualquer tipo que você desejar, desde que você forneça uma função f que pega o primeiro e retorna o último.Aqui está um exemplo de implementação da interface:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Antes dos genéricos, o polimorfismo era alcançado por subclassificação usando o extends palavra-chave.Com os genéricos, podemos realmente eliminar as subclasses e usar o polimorfismo paramétrico.Por exemplo, considere uma classe parametrizada (genérica) usada para calcular códigos hash para qualquer tipo.Em vez de substituir Object.hashCode(), usaríamos uma classe genérica como esta:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

Isso é muito mais flexível do que usar herança, porque podemos continuar com o tema do uso de composição e polimorfismo paramétrico sem bloquear hierarquias frágeis.

Os genéricos do Java não são perfeitos.Você pode abstrair tipos, mas não pode abstrair construtores de tipos, por exemplo.Ou seja, você pode dizer "para qualquer tipo T", mas não pode dizer "para qualquer tipo T que receba um parâmetro de tipo A".

Escrevi um artigo sobre esses limites dos genéricos Java aqui.

Uma grande vantagem dos genéricos é que eles permitem evitar subclasses.A subclasse tende a resultar em hierarquias de classes frágeis que são difíceis de estender e em classes que são difíceis de entender individualmente sem olhar para toda a hierarquia.

Antes dos genéricos você poderia ter aulas como Widget estendido por FooWidget, BarWidget, e BazWidget, com genéricos você pode ter uma única classe genérica Widget<A> isso leva um Foo, Bar ou Baz em seu construtor para lhe dar Widget<Foo>, Widget<Bar>, e Widget<Baz>.

Os genéricos evitam o impacto no desempenho do boxe e do unboxing.Basicamente, observe ArrayList vs List<T>.Ambos fazem as mesmas coisas básicas, mas List<T> será muito mais rápido porque você não precisa fazer box de/para o objeto.

Eu simplesmente gosto deles porque oferecem uma maneira rápida de definir um tipo personalizado (já que eu os uso de qualquer maneira).

Então, por exemplo, em vez de definir uma estrutura que consiste em uma string e um número inteiro, e depois ter que implementar todo um conjunto de objetos e métodos sobre como acessar um array dessas estruturas e assim por diante, você pode simplesmente criar um Dicionário

Dictionary<int, string> dictionary = new Dictionary<int, string>();

E o compilador/IDE faz o resto do trabalho pesado.Um Dicionário em particular permite usar o primeiro tipo como chave (sem valores repetidos).

O melhor benefício dos genéricos é a reutilização de código.Digamos que você tenha muitos objetos de negócios e escreverá um código MUITO semelhante para cada entidade para executar as mesmas ações.(Operações I.E Linq para SQL).

Com genéricos, você pode criar uma classe que será capaz de operar com qualquer um dos tipos que herdam de uma determinada classe base ou implementar uma determinada interface da seguinte forma:

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

Observe a reutilização do mesmo serviço considerando os diferentes tipos no método DoSomething acima.Verdadeiramente elegante!

Existem muitos outros ótimos motivos para usar genéricos em seu trabalho, este é o meu favorito.

  • Coleções digitadas - mesmo que você não queira usá-las, provavelmente terá que lidar com elas de outras bibliotecas, outras fontes.

  • Digitação genérica na criação de classes:

    public class Foo <t> {public t get () ...

  • Evitar o elenco - sempre não gostei de coisas como

    Novo comparador {public int compareto (objeto o) {if (o instanceof ClassicArabout) ...

Onde você está essencialmente verificando uma condição que só deveria existir porque a interface é expressa em termos de objetos.

Minha reação inicial aos genéricos foi semelhante à sua – “muito bagunçado, muito complicado”.Minha experiência é que depois de usá-los um pouco, você se acostuma com eles, e o código sem eles parece menos claramente especificado e menos confortável.Além disso, o resto do mundo Java os utiliza, então você terá que começar a usar o programa eventualmente, certo?

Para dar um bom exemplo.Imagine que você tem uma classe chamada Foo

public class Foo
{
   public string Bar() { return "Bar"; }
}

Exemplo 1Agora você deseja ter uma coleção de objetos Foo.Você tem duas opções, LIst ou ArrayList, ambas funcionam de maneira semelhante.

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

No código acima, se eu tentar adicionar uma classe de FireTruck em vez de Foo, o ArrayList irá adicioná-la, mas a Lista Genérica de Foo fará com que uma exceção seja lançada.

Exemplo dois.

Agora você tem suas duas listas de arrays e deseja chamar a função Bar() em cada uma.Como o ArrayList é preenchido com objetos, você deve lançá-los antes de chamar bar.Mas como a Lista Genérica de Foo só pode conter Foos, você pode chamar Bar() diretamente neles.

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}

Você nunca escreveu um método (ou uma classe) onde o conceito-chave do método/classe não estivesse fortemente vinculado a um tipo de dados específico dos parâmetros/variáveis ​​de instância (pense em lista vinculada, funções max/min, pesquisa binária , etc.).

Você nunca desejou poder reutilizar o algoritmo/código sem recorrer à reutilização de recortar e colar ou comprometer a digitação forte (por exemplo,eu quero um List de Strings, não um List de coisas que eu ter esperança são cordas!)?

É por isso que você deveria querer usar genéricos (ou algo melhor).

Não esqueça que os genéricos não são usados ​​apenas pelas classes, eles também podem ser usados ​​pelos métodos.Por exemplo, pegue o seguinte trecho:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

É simples, mas pode ser usado com muita elegância.O bom é que o método retorna tudo o que foi fornecido.Isso ajuda quando você está lidando com exceções que precisam ser lançadas novamente para o chamador:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

A questão é que você não perde o tipo passando-o por um método.Você pode lançar o tipo correto de exceção em vez de apenas uma Throwable, o que seria tudo o que você poderia fazer sem os genéricos.

Este é apenas um exemplo simples de uso para métodos genéricos.Existem algumas outras coisas interessantes que você pode fazer com métodos genéricos.O mais legal, na minha opinião, é a inferência de tipos com genéricos.Veja o exemplo a seguir (retirado do Effective Java 2nd Edition de Josh Bloch):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Isso não ajuda muito, mas reduz a confusão quando os tipos genéricos são longos (ou aninhados;ou seja Map<String, List<String>>).

A principal vantagem, como aponta Mitchel, é a digitação forte sem a necessidade de definir múltiplas classes.

Dessa forma, você pode fazer coisas como:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

Sem genéricos, você teria que converter blah[0] para o tipo correto para acessar suas funções.

o jvm lança de qualquer maneira ...ele cria implicitamente código que trata o tipo genérico como "Objeto" e cria conversões para a instanciação desejada.Os genéricos Java são apenas açúcar sintático.

Eu sei que esta é uma pergunta em C#, mas genéricos também são usados ​​em outras línguas e seus usos/objetivos são bastante semelhantes.

Uso de coleções Java genéricos desde Java 1.5.Portanto, um bom lugar para usá-los é quando você estiver criando seu próprio objeto semelhante a uma coleção.

Um exemplo que vejo em quase todos os lugares é uma classe Pair, que contém dois objetos, mas precisa lidar com esses objetos de maneira genérica.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

Sempre que você usar esta classe Pair, você pode especificar com quais tipos de objetos deseja lidar e quaisquer problemas de conversão de tipo aparecerão em tempo de compilação, em vez de tempo de execução.

Os genéricos também podem ter seus limites definidos com as palavras-chave ‘super’ e ‘extends’.Por exemplo, se você quiser lidar com um tipo genérico, mas quiser ter certeza de que ele estende uma classe chamada Foo (que possui um método setTitle):

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

Embora não seja muito interessante por si só, é útil saber que sempre que você lida com um FooManager, você sabe que ele irá lidar com tipos MyClass e que MyClass estende Foo.

Na documentação do Sun Java, em resposta a "por que devo usar genéricos?":

"Generics fornece uma maneira de comunicar o tipo de uma coleção ao compilador, para que ela possa ser verificada.Depois que o compilador conhece o tipo de elemento da coleção, o compilador pode verificar se você usou a coleção de forma consistente e pode inserir as conversões corretas nos valores retirados da coleção...O código usando genéricos é mais claro e seguro.... o compilador pode verificar em tempo de compilação se as restrições de tipo não foram violadas em tempo de execução [ênfase minha].Como o programa compila sem avisos, podemos afirmar com certeza que ele não lançará uma ClassCastException em tempo de execução.O efeito líquido do uso de genéricos, especialmente em grandes programas, é melhor legibilidade e robustez.[ênfase minha]"

Os genéricos permitem criar objetos com tipagem forte, mas você não precisa definir o tipo específico.Acho que o exemplo mais útil é List e classes semelhantes.

Usando a lista genérica você pode ter uma Lista Lista o que quiser e sempre pode referenciar a digitação forte, não precisa converter nem nada como faria com um Array ou Lista padrão.

Os genéricos permitem usar tipagem forte para objetos e estruturas de dados que devem ser capazes de conter qualquer objeto.Ele também elimina typecasts tediosos e caros ao recuperar objetos de estruturas genéricas (boxing/unboxing).

Um exemplo que usa ambos é uma lista vinculada.De que serviria uma classe de lista vinculada se pudesse usar apenas o objeto Foo?Para implementar uma lista vinculada que pode lidar com qualquer tipo de objeto, a lista vinculada e os nós em uma classe interna de nó hipotético devem ser genéricos se você quiser que a lista contenha apenas um tipo de objeto.

Se sua coleção contém tipos de valor, eles não precisam encaixar/desempacotar objetos quando inseridos na coleção, portanto seu desempenho aumenta drasticamente.Complementos interessantes como o resharper podem gerar mais código para você, como loops foreach.

Outra vantagem de usar genéricos (especialmente com coleções/listas) é que você obtém verificação de tipo em tempo de compilação.Isto é realmente útil ao usar uma Lista Genérica em vez de uma Lista de Objetos.

A única razão é que eles fornecem Digite segurança

List<Customer> custCollection = new List<Customer>;

em oposição a,

object[] custCollection = new object[] { cust1, cust2 };

como um exemplo simples.

Em resumo, os genéricos permitem especificar com mais precisão o que você pretende fazer (digitação mais forte).

Isso traz vários benefícios para você:

  • Como o compilador sabe mais sobre o que você deseja fazer, ele permite omitir muita conversão de tipo porque já sabe que o tipo será compatível.

  • Isso também fornece feedback antecipado sobre a correção do seu programa.Coisas que anteriormente teriam falhado em tempo de execução (por exemploporque um objeto não pôde ser convertido no tipo desejado), agora falhará em tempo de compilação e você poderá corrigir o erro antes que seu departamento de testes envie um relatório de bug enigmático.

  • O compilador pode fazer mais otimizações, como evitar boxe, etc.

Algumas coisas para adicionar/expandir (falando do ponto de vista do .NET):

Os tipos genéricos permitem criar classes e interfaces baseadas em funções.Isso já foi dito em termos mais básicos, mas acho que você começa a projetar seu código com classes que são implementadas de maneira independente de tipo - o que resulta em um código altamente reutilizável.

Argumentos genéricos sobre métodos podem fazer a mesma coisa, mas também ajudam a aplicar o princípio "Diga, não pergunte" à conversão, ou seja,“dê-me o que eu quero e, se não puder, diga-me por quê”.

Eu os uso, por exemplo, em um GenericDao implementado com SpringORM e Hibernate que se parece com isto

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

Ao usar genéricos, minhas implementações desses DAOs forçam o desenvolvedor a passar a eles apenas as entidades para as quais foram projetados, apenas subclassificando o GenericDao

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

Minha pequena estrutura é mais robusta (tem coisas como filtragem, carregamento lento, pesquisa).Eu apenas simplifiquei aqui para dar um exemplo

Eu, como Steve e você, disse no início "Muito bagunçado e complicado" mas agora vejo suas vantagens

Benefícios óbvios como "segurança de tipo" e "sem conversão" já foram mencionados, então talvez eu possa falar sobre alguns outros "benefícios" que espero que ajudem.

Primeiro de tudo, genéricos é um conceito independente de linguagem e, IMO, pode fazer mais sentido se você pensar em polimorfismo regular (tempo de execução) ao mesmo tempo.

Por exemplo, o polimorfismo como conhecemos no design orientado a objetos tem uma noção de tempo de execução em que o objeto chamador é descoberto em tempo de execução à medida que a execução do programa avança e o método relevante é chamado de acordo, dependendo do tipo de tempo de execução.Nos genéricos, a ideia é um pouco semelhante, mas tudo acontece em tempo de compilação.O que isso significa e como você faz uso dele?

(Vamos ficar com métodos genéricos para mantê-lo compacto) Isso significa que você ainda pode ter o mesmo método em classes separadas (como você fez anteriormente em classes polimórficas), mas desta vez eles são gerados automaticamente pelo compilador dependendo dos tipos definidos em tempo de compilação.Você parametriza seus métodos de acordo com o tipo fornecido em tempo de compilação.Então, em vez de escrever os métodos do zero para cada tipo você tem como faz no polimorfismo de tempo de execução (substituição de método), você permite que os compiladores façam o trabalho durante a compilação.Isto tem uma vantagem óbvia, pois você não precisa inferir todos os tipos possíveis que podem ser usados ​​em seu sistema, o que o torna muito mais escalável sem uma alteração de código.

As aulas funcionam praticamente da mesma maneira.Você parametriza o tipo e o código é gerado pelo compilador.

Depois de entender a idéia de "tempo de compilação", você pode usar tipos "limitados" e restringir o que pode ser passado como um tipo parametrizado por meio de classes/métodos.Assim, você pode controlar o que será transmitido, o que é algo poderoso, especialmente se você tiver uma estrutura sendo consumida por outras pessoas.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

Ninguém pode definir nada além de MyObject agora.

Além disso, você pode "impor" restrições de tipo aos argumentos do método, o que significa que você pode garantir que ambos os argumentos do método dependam do mesmo tipo.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

Espero que tudo isso faça sentido.

Certa vez, dei uma palestra sobre esse assunto.Você pode encontrar meus slides, código e gravação de áudio em http://www.adventuresinsoftware.com/generics/.

Usar genéricos para coleções é simples e limpo.Mesmo se você apostar em qualquer outro lugar, o ganho das coleções é uma vitória para mim.

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

contra

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

ou

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

Só isso já vale o “custo” marginal dos genéricos, e você não precisa ser um Guru genérico para usar isso e obter valor.

Os genéricos também oferecem a capacidade de criar objetos/métodos mais reutilizáveis, ao mesmo tempo que fornecem suporte específico ao tipo.Você também ganha muito desempenho em alguns casos.Não conheço as especificações completas dos Java Generics, mas no .NET posso especificar restrições no parâmetro Type, como Implements a Interface, Constructor e Derivation.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top