Como usar a classe <T> em Java?

247

Há uma boa discussão sobre genéricos e o que eles realmente fazem nos bastidores nesta pergunta , então todos sabemos que Vector<int[]>é um vetor de matrizes inteiras e HashTable<String, Person>uma tabela cujas chaves são cadeias e valores Persons. No entanto, o que me impressiona é o uso de Class<>.

A classe java Classtambém deve usar um nome de modelo (ou pelo menos estou sendo informada pelo sublinhado amarelo no eclipse). Não entendo o que devo colocar lá. O ponto principal do Classobjeto é quando você não possui totalmente as informações sobre um objeto, para reflexão e outras coisas. Por que isso me faz especificar qual classe o Classobjeto manterá? Eu claramente não sei, ou não estaria usando oClass objeto, usaria o específico.

Karl
fonte

Respostas:

136

O uso da versão gerada da classe Class permite, entre outras coisas, escrever coisas como

Class<? extends Collection> someCollectionClass = someMethod();

e então você pode ter certeza de que o objeto de classe que você recebe se estende Collectione uma instância dessa classe será (pelo menos) uma coleção.

Yuval
fonte
183

Tudo o que sabemos é " Todas as instâncias de qualquer classe compartilham o mesmo objeto java.lang.Class desse tipo de classe "

por exemplo)

Student a = new Student();
Student b = new Student();

Então a.getClass() == b.getClass()é verdade.

Agora assuma

Teacher t = new Teacher();

sem genéricos, o abaixo é possível.

Class studentClassRef = t.getClass();

Mas isso está errado agora ..?

por exemplo) public void printStudentClassInfo(Class studentClassRef) {}pode ser chamado comTeacher.class

Isso pode ser evitado usando genéricos.

Class<Student> studentClassRef = t.getClass(); //Compilation error.

Agora, o que é T ?? T é parâmetros de tipo (também chamados de variáveis ​​de tipo); delimitado por colchetes angulares (<>), segue o nome da classe.
T é apenas um símbolo, como um nome de variável (pode ser qualquer nome) declarado durante a gravação do arquivo de classe. Posteriormente, T será substituído por
um nome de classe válido durante a inicialização ( HashMap<String> map = new HashMap<String>();)

por exemplo) class name<T1, T2, ..., Tn>

Então Class<T>representa um objeto de classe de tipo de classe específico 'T '.

Suponha que seus métodos de classe funcionem com parâmetros de tipo desconhecido, como abaixo

/**
 * Generic version of the Car class.
 * @param <T> the type of the value
 */
public class Car<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Aqui T pode ser usado como Stringtipo como CarName

OR T pode ser usado como Integertipo como modelNumber ,

OR T pode ser usado como Objecttipo como instância de carro válida .

Agora, aqui está o POJO simples, que pode ser usado de maneira diferente em tempo de execução.
Coleções, por exemplo) List, Set, Hashmap são os melhores exemplos que funcionarão com objetos diferentes conforme a declaração de T, mas uma vez que declaramos T como String,
por exemplo) HashMap<String> map = new HashMap<String>();Então, ele aceitará apenas objetos de instância da Classe String.

Métodos genéricos

Métodos genéricos são métodos que apresentam seus próprios parâmetros de tipo. Isso é semelhante a declarar um tipo genérico, mas o escopo do parâmetro type é limitado ao método em que é declarado. Métodos genéricos estáticos e não estáticos são permitidos, bem como construtores de classes genéricos.

A sintaxe de um método genérico inclui um parâmetro de tipo, entre colchetes angulares, e aparece antes do tipo de retorno do método. Para métodos genéricos, a seção de parâmetro type deve aparecer antes do tipo de retorno do método.

 class Util {
    // Generic static method
    public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

 class Pair<K, V> {

    private K key;
    private V value;
}

Aqui <K, V, Z, Y>está a declaração dos tipos usados ​​nos argumentos do método que deveriam antes do tipo de retorno que está booleanaqui.

No abaixo; declaração de tipo <T>não é necessária no nível do método, pois já está declarada no nível da classe.

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

Mas abaixo está errado, pois os parâmetros do tipo de classe K, V, Z e Y não podem ser usados ​​em um contexto estático (método estático aqui).

class Util <K, V, Z, Y>{
    // Generic static method
    public static  boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

OUTROS CENÁRIOS VÁLIDOS SÃO

class MyClass<T> {

        //Type declaration <T> already done at class level
        private  T myMethod(T a){
            return  a;
        }

        //<T> is overriding the T declared at Class level;
        //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. 
        private <T> T myMethod1(Object a){
                return (T) a;
        }

        //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).  
        private T myMethod1(Object a){
                return (T) a;
        }       

        // No ClassCastException        
        // MyClass<String> obj= new MyClass<String>();
        // obj.myMethod2(Integer.valueOf("1"));
        // Since type T is redefined at this method level.
        private <T> T myMethod2(T a){
            return  a;
        }

        // No ClassCastException for the below
        // MyClass<String> o= new MyClass<String>();
        // o.myMethod3(Integer.valueOf("1").getClass())
        // Since <T> is undefined within this method; 
        // And MyClass<T> don't have impact here
        private <T> T myMethod3(Class a){
            return (T) a;
        }

        // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
        // Should be o.myMethod3(String.valueOf("1").getClass())
    private  T myMethod3(Class a){
        return (T) a;
    }


        // Class<T> a :: a is Class object of type T
        //<T> is overriding of class level type declaration; 
        private <T> Class<T> myMethod4(Class<T> a){
            return  a;
        }
    }

E, finalmente, o método estático sempre precisa de <T>declaração explícita ; Não deriva do nível de classe Class<T>. Isso ocorre porque o nível de classe T está vinculado à instância.

Leia também Restrições sobre genéricos

Curingas e Subtipagem

argumento de tipo para um método genérico

Kanagavelu Sugumar
fonte
2
Minha resposta sobre curingas limitadas stackoverflow.com/questions/1368166/...
Kanagavelu Sugumar
"A classe <T> representa um objeto de classe do tipo específico de classe 'T'." Que faz sentido. Obrigado ..
pornn022 25/05
Esta resposta é terrivelmente confusa na maneira como usa Classes (da escola) em uma pergunta sobre Classes (em Java). É difícil saber do que o autor está falando de uma frase para outra.
Echox
@Echox Sinto muito, posso melhorá-lo, se você tiver alguma dúvida específica.
Kanagavelu Sugumar
32

Na documentação Java:

[...] Surpreendentemente, a classe Class foi gerada. Os literais de classe agora funcionam como tokens de tipo, fornecendo informações sobre os tipos de tempo de execução e tempo de compilação. Isso permite um estilo de fábricas estáticas exemplificadas pelo método getAnnotation na nova interface AnnotatedElement:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

Este é um método genérico. Ele infere o valor do parâmetro de tipo T do argumento e retorna uma instância apropriada de T, conforme ilustrado pelo seguinte trecho:

Author a = Othello.class.getAnnotation(Author.class);

Antes dos genéricos, você teria que transmitir o resultado ao autor. Além disso, você não teria como fazer com que o compilador verifique se o parâmetro real representa uma subclasse de Anotação. [...]

Bem, eu nunca tive que usar esse tipo de coisa. Qualquer um?

raupach
fonte
2
Eu pensei que eu fiz. Uma estrutura (das sortes) com a qual trabalhei exigia que você passasse o nome da classe de serviços em que seu módulo dependia. Eu construí uma camada sobre a que levava objetos Class, para restringir a quantidade de opções. Usando a Class<? extends X>notação que imaginei, poderia limitá-la apenas aos tipos de 'serviço'. Exceto que não havia um tipo comum de 'serviço', então eu só poderia fazê-lo Class<?>. Alas.
Fwielstra
9

Eu achei class<T>útil quando crio pesquisas de registro de serviço. Por exemplo

<T> T getService(Class<T> serviceClass)
{
    ...
}
Kire Haglin
fonte
5

Como outras respostas apontam, há muitas e boas razões pelas quais isso classfoi tornado genérico. No entanto, há muitas vezes que você não tem como saber o tipo genérico para usar Class<T>. Nesses casos, você pode simplesmente ignorar os avisos do eclipse amarelo ou pode usar Class<?>... É assim que eu faço;)

bruno conde
fonte
@SuppressWarnings("unchecked")vem para o resgate! (Apenas tenha cuidado para sempre aplicá-lo ao menor escopo possível, pois isso oculta problemas em potencial no seu código.)
Donal Fellows
4

Seguindo a resposta de @Kire Haglin, um outro exemplo de métodos genéricos pode ser visto na documentação para descomposição de JAXB :

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

Isso permite unmarshalretornar um documento de um tipo arbitrário de árvore de conteúdo JAXB.

Ensopado
fonte
2

Você costuma usar curingas com Class. Por exemplo Class<? extends JComponent>,, permitiria especificar que a classe é uma subclasse de JComponent. Se você recuperou a Classinstância de Class.forName, poderá usar Class.asSubclassa conversão antes de tentar, por exemplo, construir uma instância.

Tom Hawtin - linha de orientação
fonte
2

Em java <T>significa classe genérica. Uma classe genérica é uma classe que pode funcionar em qualquer tipo de dados ou, em outras palavras, podemos dizer que é independente do tipo de dados.

public class Shape<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Onde T significa tipo. Agora, quando você cria uma instância dessa classe Shape, precisará informar ao compilador qual tipo de dados ele estará trabalhando.

Exemplo:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

Inteiro é um tipo e String também é um tipo.

<T>especificamente significa tipo genérico. De acordo com o Java Docs - Um tipo genérico é uma classe ou interface genérica parametrizada sobre os tipos.

Frostbyte1010
fonte
0

Apenas para dar outro exemplo, a versão genérica de Class ( Class<T>) permite escrever funções genéricas, como a abaixo.

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}
zeronona
fonte
-2

É confuso no começo. Mas ajuda nas situações abaixo:

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.
fastcodejava
fonte
4
Não é apenas uma maneira incrivelmente complicada de dizer Ação a = nova Action ()?
Karl
1
Action a = new Action() ? ActionEm uma interface, SomeActionestamos tentando obter uma instância do. Temos apenas o nome de SomeActiondisponível em tempo de execução.
fastcodejava
Isso não passa pela digitação: o compilador java não tem como dizer que <code> Class.forName ("SomeAction") </code> será do tipo <code>Class<Action> </code>, pois isso será apenas ser conhecido em tempo de execução.
tonio 23/11/10
@tonio, certo, então você provavelmente precisará envolver essa primeira linha em algum tipo de tentativa / captura. Mas, supondo que nenhuma exceção seja lançada lá, é garantido que a segunda linha funcione.
MatrixFrog
2
O que você realmente está descrevendo é que SomeAction.class corresponde ao padrão Class <? estende Action> - ou seja, se você tiver um método useAction (classe <? estende Action> klass), poderá chamar useAction (SomeAction.class).
Thomas Andrews
-5

Basta usar a classe de carne bovina:

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}
yaa
fonte
4
Isso realmente não fornece uma resposta completa para a pergunta. Se você acha que tem algo a acrescentar, edite esta resposta.
MasterAM