Я хотел бы знать, как (если возможно) я могу создать класс, который является определенным типом родового класса.
Чтобы быть конкретным, у меня есть abstract class Stack<Type>
со всеми необходимыми методами, и я хотел бы иметь class StackInteger
то время как экземпляр StackInteger
также является экземпляром Stack<Integer>
.
Я вижу, я могу сделать что-то вроде этого:
class StackInteger {
Stack<Integer> s;
public StackInteger(int size) {
s = new Stack<Integer>(size);
}
}
а затем реализовать все методы Stack, чтобы содержать s.method();
но я задаюсь вопросом, могу ли я сделать это легко, простираясь от Stack.
Да, в этом случае наследование является лучшим решением, чем состав, как у вас, потому что StackInteger
- это Stack
.
Просто поставьте Integer
как аргумент типа в предложении extends
.
class StackInteger extends Stack<Integer>
Однако, если вы не предоставляете каких-либо дополнительных функций помимо того, что уже есть в Stack
, этот класс бесполезен, и вы можете просто использовать Stack<Integer>
.
Stack<Integer>
предпочтите эквивалентную реализацию Deque
поскольку она обеспечивает более полный и согласованный набор операций стека LIFO.
Я бы рекомендовал не распространять общие классы, чтобы преодолеть многословность. Это антипаттерн, известный как антипаттер псевдотипа.
Из статьи:
если параметр метода имеет тип StringList, вы не можете передать ему обычный список. Это означает, что вы вообще не можете использовать псевдотипы как аргументы метода, не требуя, чтобы каждое использование этого метода использовало псевдотип, что в практическом смысле означает, что вы вообще не можете использовать псевдотипы в библиотечных API.
...
Еще одна проблема с antipattern псевдо-typedef заключается в том, что он имеет тенденцию игнорировать преимущество использования интерфейсов для определения типов переменных и аргументов метода. Хотя можно определить StringList как интерфейс, который расширяет List и конкретный тип StringArrayList, который расширяет ArrayList и реализует StringList, большинство пользователей антипаттера псевдоспециалистов обычно не идут на эту длину, так как цель этой техники заключается в том, чтобы упростить и сократить имена типов. В результате API будут менее полезными и более хрупкими, поскольку они используют конкретные типы, такие как ArrayList, а не абстрактные типы, такие как List.
Итак, почему бы не придерживаться Stack<Integer>
? Я не думаю, что это так плохо.
Да, ты можешь сделать это.
class StackInteger extends Stack<Integer> {
....
}
Как указывает @Magnamag, это часто считается антипаттерном. Это особенно верно, если это делается только для того, чтобы не указывать параметры типа.
Однако для этого могут быть законные способы. Например, если не-общий класс является private
, пользователи класса будут вынуждены программировать интерфейс (или абстрактный класс) в любом случае. В этом примере (по общему признанию, довольно надуманный) существует статический метод, возвращающий не общий универсальный класс, расширяющий AbstractList<String>
.
public class Main {
public static List<String> helloWorld() {
return new AbstractList<String>() {
@Override
public String get(int i) {
switch (i) {
case 0: return "Hello";
case 1: return "World";
default: throw new ArrayIndexOutOfBoundsException();
}
}
@Override
public int size() {
return 2;
}
};
}
public static void main(String[] args) {
System.out.println(helloWorld());
}
}
Stack<Integer>
с двумя меньшими символами ...