Все, що ми знаємо, це " Усі екземпляри будь-якого класу мають один і той же об'єкт java.lang.Class цього типу класу "
наприклад)
Student a = new Student();
Student b = new Student();
Тоді a.getClass() == b.getClass()
це правда.
Тепер припустимо
Teacher t = new Teacher();
без дженериків можливе нижче.
Class studentClassRef = t.getClass();
Але це зараз неправильно ..?
напр.) public void printStudentClassInfo(Class studentClassRef) {}
можна викликати сTeacher.class
Цього можна уникнути, використовуючи дженерики.
Class<Student> studentClassRef = t.getClass(); //Compilation error.
Тепер що таке T ?? T - параметри типу (також називаються змінними типу); розмежоване кутовими дужками (<>), слід за назвою класу.
T - просто символ, подібно до імені змінної (може бути будь-яке ім'я), оголошеного під час написання файлу класу. Пізніше T буде замінено
дійсним іменем класу під час ініціалізації ( HashMap<String> map = new HashMap<String>();
)
наприклад) class name<T1, T2, ..., Tn>
Отже, Class<T>
представляє об’єкт класу конкретного типу класу 'T
'.
Припустимо, що методи вашого класу повинні працювати з невідомими параметрами типу, як показано нижче
/**
* 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; }
}
Тут T може використовуватися як String
тип CarName
АБО T можна використовувати як Integer
тип як модельNumber ,
АБО Т може використовуватися як Object
тип як дійсний екземпляр автомобіля .
Тепер ось вище - простий POJO, який можна використовувати по-різному під час виконання.
Колекції, наприклад) Список, Set, Hashmap - найкращі приклади, які працюватимуть з різними об'єктами відповідно до декларації T, але як тільки ми оголосили T як String,
наприклад) HashMap<String> map = new HashMap<String>();
Тоді він буде приймати лише об'єкти екземпляра String Class.
Родові методи
Загальні методи - це методи, що вводять параметри власного типу. Це схоже на оголошення загального типу, але область параметра параметра обмежена методом, де він оголошений. Допускаються статичні та нестатичні загальні методи, а також конструктори родового класу.
Синтаксис загального методу включає параметр типу, що знаходиться у внутрішніх кутових дужках, і з'являється перед типом повернення методу. Для загальних методів перед типом повернення методу повинен з’явитися розділ параметра типу.
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;
}
Ось <K, V, Z, Y>
декларація типів, що використовуються в аргументах методу, які повинні бути перед типом повернення, який знаходиться boolean
тут.
Внизу; Декларація типу <T>
не потрібна на рівні методу, оскільки вона вже оголошена на рівні класу.
class MyClass<T> {
private T myMethod(T a){
return a;
}
}
Але нижче це неправильно, оскільки параметри типу K, V, Z та Y на рівні класу не можуть використовуватися в статичному контексті (статичний метод тут).
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());
}
}
ІНШІ ВАЛИДНІ СЦЕНАРІЇ Є
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;
}
}
І нарешті, статичний метод завжди потребує явного <T>
декларування; Це не походить від рівня класу Class<T>
. Це пояснюється тим, що рівень класу T пов'язаний з екземпляром.
Читайте також обмеження на дженерики
Підстановки та підтипи
аргумент типу для загального методу