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 String
tipo como CarName
OR T pode ser usado como Integer
tipo como modelNumber ,
OR T pode ser usado como Object
tipo 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á boolean
aqui.
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