Возможно создание специализированных возможностей 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 здесь.