Возможно создание специализированных возможностей Java (шаблонов) (переопределение типов шаблонов с определенными типами)
Мне интересно, какие параметры специализируются на общих типах в Java, то есть в шаблоном классе, чтобы иметь определенные переопределения для определенных типов.
В моем случае я был родовым классом (типа T) для возврата null обычно, но возвращаю "" (пустую строку), когда T является типом String или 0 (ноль), когда его тип Integer и т.д..
Простое предоставление перегрузки типа для метода вызывает ошибку "метод является неоднозначной":
например:.
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Единственная опция для подкласса общего класса с определенным типом (см. StringBar в следующем примере?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Это единственный способ, это немного больно, чтобы создать подкласс для каждого типа, который я хочу специализировать, вместо перегрузки get() в классе Bar.
Я предполагаю, что могу проверить instanceof в методе Bar.get(), например. T get (T t) { if (t instanceof String) return ""; if (t instanceof Integer) возвращает 0; else возвращает null; }
Однако меня научили избегать экземпляра и использовать полиморфизм, когда это возможно.
Ответы
Ответ 1
С учетом всех обстоятельств, консенсус, по-видимому, заключается в том, что метод метода StringBar, упомянутый в вопросе, является единственным способом.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Ответ 2
Компилятор действительно корректен, поскольку следующий код проверяется временем компиляции (Bar<String> barString = new Bar<String>();
), из
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
и неоднозначно, поскольку у вас не может быть двух одинаковых методов с одинаковыми типами возврата и теми же аргументами параметров.
См. объяснение Джона Скита:
Вы можете подклассом Bar<T>
и создать StringBar
(обратите внимание, что я удалил ключевое слово static
) и переопределил метод get()
.
public class BarString extends Bar<String> {
@Override
public String get(String x) {
return "";
}
}
Ответ 3
Обобщения в Java в этом отношении сильно отличаются от шаблонов на С++. Невозможно написать определенную версию универсального класса, чтобы сделать что-то другое для конкретного случая, как это может сделать С++. Во время выполнения также невозможно определить, что такое T, - это потому, что эта информация не передается в код байта (объектный код) и поэтому даже не существует во время выполнения. Это связано с тем, что называется стиранием типа.
BarString и BarInt были бы очевидным способом сделать это, но есть улучшения, которые вы можете сделать. Например, вы можете написать общий бар для покрытия распространенных случаев, а затем написать специализированные BarString и BarInt для реализации особых случаев. Убедитесь, что экземпляры могут быть созданы только с помощью factory, который принимает класс обрабатываемого объекта:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
Это, вероятно, не будет компилироваться, но у меня нет времени на точный синтаксис. Это иллюстрирует принцип.
Ответ 4
Дженерики в Java не предназначены для специализации. Они созданы для обобщения! Если вы хотите специализироваться на определенных типах, вы должны специализироваться... через подкласс.
Часто вам не нужно что-то делать в специализированной манере. Ваш пример StringBar является своего рода надуманным, потому что вы могли бы это сделать:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
Я не понимаю, почему вам нужно специализироваться на String здесь.