Шаблони аргументів (Джокер в Java Generics)
Шаблон аргументів вказується символом ? і являє собою невідомий тип.
boolean sameAvg(Stats<?> ob) ...
boolean sameAvg(Stats<?> ob) ...
? (знак питання) символ представляє підстановлювальний елемент, тобто будь-який тип. Якщо ми пишемо <? extends Number>, це означає, що будь-який дочірній клас числа, наприклад, Integer, Float, подвійний і т.д. І можна викликати метод класу Number через будь-який об'єкт класу object.
Приклад простого типу-контейнера Box, який підтримує операції put і get.Box параметризований параметром T.
public interface Box<T> {
public T get();
public void put(T element);
}
Приклад методу unbox з wildcard-параметром (<?>)public void unbox (Box <?> box)
{
System.out.println (box.get ());
}
{
System.out.println (box.get ());
}
Цей параметр може викликати метод get (), а може - будь-який з методів, успадкованих від Object (наприклад, hashCode ()). Єдине, що він не може зробити - викликати метод put (), тому що без знання типу параметра T для даного екземпляра класу Box компілятор не в змозі перевірити цю операцію на безпеку (взагалі ж, викликати put () можна тільки в одному випадку: якщо передати туди null; ми можемо не знати, який тип представлений T, але зате знаємо, що літерал null є допустимим значенням для будь-якого заданого типу).
Що знає unbox () про тип значення, що повертається у метод box.get ()? Він знає, що це тип T для якогось невідомого T. Тому краще, що він може зробити - прийняти рішення, згідно з яким тип значення від get () є заміщенням невідомого раніше типу T. У разі необмеженого wildcard цей тип представлений типом Object . Таким чином, вираз box.get () в попередньому прикладі повертає значення типу Object.
Приклад.
import java.util.*;
abstract class Shape{
abstract void draw();
}
class Rectangle extends Shape{
void draw(){System.out.println("drawing rectangle");}
}
class Circle extends Shape{
void draw(){System.out.println("drawing circle");}
}
class GenericTest{
//creating a method that accepts only child class of Shape
public static void drawShapes(List<? extends Shape> lists){
for(Shape s:lists){
s.draw();//calling method of Shape class by child class instance
}
}
public static void main(String args[]){
List<Rectangle> list1=new ArrayList<Rectangle>();
list1.add(new Rectangle());
List<Circle> list2=new ArrayList<Circle>();
list2.add(new Circle());
list2.add(new Circle());
drawShapes(list1);
drawShapes(list2);
}}
Output:
drawing rectangle
abstract class Shape{
abstract void draw();
}
class Rectangle extends Shape{
void draw(){System.out.println("drawing rectangle");}
}
class Circle extends Shape{
void draw(){System.out.println("drawing circle");}
}
class GenericTest{
//creating a method that accepts only child class of Shape
public static void drawShapes(List<? extends Shape> lists){
for(Shape s:lists){
s.draw();//calling method of Shape class by child class instance
}
}
public static void main(String args[]){
List<Rectangle> list1=new ArrayList<Rectangle>();
list1.add(new Rectangle());
List<Circle> list2=new ArrayList<Circle>();
list2.add(new Circle());
list2.add(new Circle());
drawShapes(list1);
drawShapes(list2);
}}
Output:
drawing rectangle
drawing circle
drawing circle