Java Generics Wildcarding з кількома класами


385

Я хочу мати об’єкт Class, але я хочу змусити будь-який клас, який він представляє, розширити клас A та реалізувати інтерфейс B.

Я можу зробити:

Class<? extends ClassA>

Або:

Class<? extends InterfaceB>

але я не можу робити обох. Чи є спосіб це зробити?

Відповіді:


613

Власне, ти можеш робити те, що хочеш. Якщо ви хочете надати декілька інтерфейсів або інтерфейси класу плюс, ви повинні мати ваш підказчик виглядати приблизно так:

<T extends ClassA & InterfaceB>

Дивіться підручник з генерики на сайті sun.com, зокрема в розділі Параметри обмеженого типу , внизу сторінки. Насправді ви можете перелічити більше одного інтерфейсу, якщо бажаєте, використовуючи & InterfaceNameдля кожного потрібний вам.

Це може бути довільно складним. Для демонстрації див. Декларацію JavaDoc Collections#max, яка (загорнута на два рядки):

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

чому так складно? Як сказано в FAQ Generics FAQ: Для збереження бінарної сумісності .

Схоже, це не працює для змінної декларації, але вона працює, коли ставить загальний кордон класу. Таким чином, щоб робити те, що ви хочете, можливо, вам доведеться перестрибнути через кілька обручів. Але ти можеш це зробити. Ви можете зробити щось подібне, поставивши загальний кордон на своєму класі, а потім:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

щоб отримати, variableщо має обмеження, яке ви хочете. Для отримання додаткової інформації та прикладів, перегляньте сторінку 3 Generics в Java 5.0 . Зверніть увагу, що <T extends B & C>вперше ім'я класу повинно бути першим, а інтерфейси слідувати. І звичайно, ви можете перелічити лише один клас.


94
Це корисно. Варто зазначити, що клас повинен бути першим, ви не можете сказати "<T розширює інтерфейсB & ClassA>".
EricS

5
Як зробити те ж саме для T, слід або розширити клас АБО реалізувати інтерфейс?
Рагунат Джавахар

1
@RagunathJawahar: Ви не можете надто відкритими з цього приводу. У вас повинні бути деякі межі, і один з них заздалегідь знає, де у вас буде інтерфейс і де ви будете мати класи, і як ви будете використовувати спадщину. Ви майже повинні знати параметр типу, якщо це буде клас чи інтерфейс.
Едді

4
Перший рядок вашої відповіді говорить про те, "щоб ваш підстановник виглядав приблизно так". У ?цьому виразі немає, тож це справді "майна"? Я запитую, тому що я не можу змусити всю концепцію "розширення двох речей" працювати, коли параметр типу справді є малим символом.
The111

1
Так, це насправді не є простою карткою, і ти не можеш робити те, що просив ОП із малим символом. Ви можете робити те, що хотів ОП, ефективно, тільки не за допомогою підстановки.
Едді

17

Ви не можете це зробити з параметрами типу "анонімний" (тобто, макіяжними символами, які використовуються ?), але ви можете зробити це за допомогою "name" параметрів типу. Просто оголосіть параметр типу на рівні методу чи класу.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}

2
Чому заборонені символи заборонені? тобто я не бачу, чому це не повинно бути дійсним: ArrayList <? розширює список ClassA та InterfaceB>; І тоді, якщо ви витягли елемент зі списку, ви можете призначити його змінній типу ClassA або типу InterfaceB
Марк

Заміна підстановки на змінну - це чудова техніка! Але це не завжди працює. Наприклад, Java не допускає змінних типів імен у типах значень анотацій, в той час як вона дозволяє дозволити.
Підписався
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.