واضح است که در مثال اول کامپایلر خطا می دهد چون E فقط می تواند از نوع Test و یا کلاس هایی که Test را extends می کنند باشد و این صراحتا به کامپایلر گفته شده اما در مثال دوم نوع E مشخص نیست و این دو نوع تعریف کاربردهای مختلفی دارد بعنوان نمونه شما در مثال اول چون نوع مشخص است می توانید به متد های کلاس Test دسترسی داشته باشید
public <E extends Test> void f(E e) {
e.printTest();
}
پس دیگری نیازی به تعریف
public void f(Test test) {
test.printTest();
}
ندارید
اما در مثال دوم شما فقط در دو صورت می توانید به متدها دسترسی داشته باشید با استفاده از reflection یا cast کردن به تایپ مورد نظر .
public <E> void f(E e) {
if(e.getClass().equals(Test.class)
((Test)e).printTest();
}
اما در این روش هر تایپی غیر از Test نیز می توانید به آن پاس کنید .
public <E> void f(E e) {
if(e.getClass().equals(Test.class)
((Test)e).printTest();
if(e.getClass().equals(Person.class)
((Person)e).printName();
}
پس نتیجه می گیریم که دو مثالی که شما زدید دو چیز کاملا متفاوت است .