Ограничение типов переменных
На месте типа Т может оказаться любой тип, это может
привести к ошибкам. Например, если в коде обобщенного класса сортируются или сравниваются объекты типа Т, но реальный тип Т не реализует интерфейс Comparable и
компилятор не может это проверить.
Выход – ограничение типов переменных
public static <Т extends Comparable> T min(T[] a)
public static <T extends Comparable & Comparator> T min(T[] a)
(только один класс и много интерфейсов)
Ограничение типов переменных
Особенности:
1.Обобщенные типы существуют только на уровне компилятора.
2.Виртуальная машина ничего не знает об обобщенных типах.
3.После проверки компилятора выполняется
«зачистка» типа.
4.Все переменные типы приводятся к своим ограничителям. Если ограничителей нет, то к
Object
Ограничение типов переменных
public class Pair {
private Object first; private Object second; public Pair() {};
public Pair(Object first, Object second){}; public Object getFirst(){ return first; } public void setFirst(Object first){}
}
При зачистке операции приведения типов для
возвращаемых и передаваемых параметров
вставляются автоматически.
Для обобщенных методов также автоматически осуществляется подчистка.
Подчистка типов и полиморфизм
Подчистка типов конфликтует с полиморфизмом.
public class Pair<T> { private T first; public Pair () {};
public Pair(T first){};
public T getFirst() { return first; }
public void setFirst(T first){ this.first = first;)
}
public class DatePair extends Pair<Date> { @Override
public void setFirst(Date first){}
}
public class Main {
public static void main (String[] args) { Pair<Date> pair;
DatePair datePair = new DatePair(); pair = datePair;
pair.setFirst(new Date());
}
}
Подчистка типов и полиморфизм
На самом деле в классе DatePair нет переопределения
метода setFirst.
Вклассе будет два метода:
//пришел из родителя после подчистки void setFirst(Object first)
void setFirst(Date first)
Так как на этапе компиляции pair.setFirst(new Date)
ничего не знает о дочернем классе, то будет подставлен
поиск метода void setFirst(Object first), то есть метода
родителя, что нарушает принцип полиморфизма
Подчистка типов и полиморфизм
Для решения этой проблемы компилятор
автоматически генерирует методы-мостики.
public void setFirst(Object first) { setFirst((Date)first);
}
Подчистка типов и полиморфизм
Более «интересная» проблема возникает в таком случае:
в обобщенном родителе |
– Т getFirst(){} |
в дочернем классе |
– Date getFirst(){} |
После выполнения подчистки и наследования в
дочернем классе получаем:
Object getFirst(){} Date getFirst(){}
Два одинаковых метода с разными типами возврата запрещены, но разрешены – так как в виртуальной машине идентификатор метода включает и тип возвращаемого результата, то в данном случае компилятор пропустит код.
Подчистка типов и полиморфизм
// Итак есть класс родитель public class ParentClass<T> {
private T first;
public T getFirst(){ return first; }
public void setFirst(T first){ this.first = first; }
}
//есть потомок
public class ChildrenClass extends ParentClass<Date>{ public Date getFirst(){ return new Date(); } public void setFirst(Date d){ }
}
// и класс Main public class Main {
public static void main(String[] args) { ParentClass<Date> p = new ParentClass<Date>(); ChildrenClass с = new ChildrenClass();
p = c;
p.setFirst(new Date()); p.getFirst();
}
Подчистка типов и полиморфизм
Если дизассемблировать код классов, то увидим следующее.
Для Main.class:
public static void main(java.lang.StringQ); Code:
0: |
new #2; |
//class javaapplication19/ParentClass |
3: |
dup |
|
4: |
invokespecial #3; //Method javaapplication19/ParentClass."<init>":()V |
|
7: |
astore_1 |
|
8: |
new #4; |
//class javaapplication19/ChildrenClass |
11: dup |
|
|
12: invokespecial #5; //Method javaapplication19/ChildrenClass."<init>":()V |
||
15: astore_2 |
|
|
16: aload_2 |
|
|
17: astore_1 |
|
|
18: aload_1 |
|
|
19: new #6;//class java/util/Date |
||
22: dup |
|
|
23: invokespecial |
#7; //Method java/util/Date."<init>":()V |
|
26: invokevirtual |
#8; // Method javaapplication19/ParentClass.setFirst:(Ljava/lang/Object;)V |
|
28: invokevirtual |
#10;// Method javaapplication19/ParentClass.getFirst:()Ljava/lang/Object; |
|
30: return |
|
В строках 26 и 28 вызываются методы, которые содержат в сигнатуре Object
Подчистка типов и полиморфизм
Теперь для класса потомка
public javaapplication19.ChildrenClass(); Code:
0: aload_0
1: invokespecial #1; //Method javaapplicationl 9/ParentClass."<init>":()V
4: return
// Это родной метод
public void setFirst(java.util.Date); Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_1
4: invokevirtual #3; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
7: return
// Это родной метод
public java.util.Date getFirst(); Code:
0: new #4; //class java/util/Date
3: dup
4: invokespecial #5; //Method java/util/Date."<init>":()V
7: areturn