Могу ли я отразить типичный тип в java?

Возможно ли отражать типичный тип в Java? Используя описанную технику здесь Я получаю сообщение об ошибке, потому что токены классов не могут быть общими. Возьмите пример ниже. Я хочу создать экземпляр некоторого подкласса Творца, который реализует Творца. Фактическое имя класса передается как аргумент командной строки. Идея состоит в том, чтобы иметь возможность указать реализацию Creator во время выполнения. Есть ли другой способ выполнить то, что я пытаюсь сделать здесь?

public interface Creator<T> {
    T create();
}
public class StringCreator implements Creator<String> {
    public String create() { return new String(); }
}
public class FancyStringCreator implements Creator<String> {
    public String create() { return new StringBuffer().toString(); }
}
public static void main(String[] args) throws Exception {
    Class<?> someClass = Class.forName(args[0]);
    /*ERROR*/Class<? extends Creator<String>> creatorClass = someClass.asSubclass(Creator.class);
    Constructor<? extends Creator<String>> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
    Creator<String> creator = creatorCtor.newInstance((Object[]) null);
}

Изменить: мне нравится подход Маркуса как самый простой и прагматичный, не обойдя все дженерики. Я могу использовать его в своей ситуации, потому что могу указать, что переданный класс должен быть подклассом StringCreator. Но по мере того как Эриксон указывал, что общая информация все еще присутствует на уровне типа, просто не на уровне времени выполнения, так что все еще можно рефлексивно исследовать, реализует ли данный класс правильный общий тип.

Ответ 1

Общая информация теряется во время выполнения. Существует не эквивалент времени исполнения Creator <String> .class. Вы можете создать тип между Creator и StringCreator, который исправляет общий тип:

public interface Creator<T> {
        T create();
}
public interface StringCreator extends Creator<String> { }
public class StringCreatorImpl implements StringCreator  {
        public String create() { return new String(); }
}
public class FancyStringCreator implements StringCreator  {
        public String create() { return new StringBuffer().toString(); }
}
public static void main(String[] args) throws Exception {
        Class<?> someClass = Class.forName(args[0]);
        Class<? extends StringCreator> creatorClass = someClass.asSubclass(StringCreator.class);
        Constructor<? extends StringCreator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
        Creator<String> creator = creatorCtor.newInstance((Object[]) null);
}

Но, конечно, вы теряете немного гибкости, потому что не можете использовать следующий класс создателя:

public class AnotherCreator implements Creator<String> {
    public String create() { return ""; }
}

Ответ 2

Это будет делать то, что вы пытаетесь сделать, обеспечивая безопасность типа. Нет никакого способа избежать непроверенного предупреждения, но проверка типа, сделанная здесь, оправдывает его подавление.

  public static void main(String[] args)
    throws Exception
  {
    Class<? extends Creator<String>> clz = load(argv[0], String.class);
    Constructor<? extends Creator<String>> ctor = clz.getConstructor();
    Creator<String> creator = ctor.newInstance();
    System.out.println(creator.create());
  }

  public static <T> Class<? extends Creator<T>> load(String fqcn, Class<T> type)
    throws ClassNotFoundException
  {
    Class<?> any = Class.forName(fqcn);
    for (Class<?> clz = any; clz != null; clz = clz.getSuperclass()) {
      for (Object ifc : clz.getGenericInterfaces()) {
        if (ifc instanceof ParameterizedType) {
          ParameterizedType pType = (ParameterizedType) ifc;
          if (Creator.class.equals(pType.getRawType())) {
            if (!pType.getActualTypeArguments()[0].equals(type))
              throw new ClassCastException("Class implements " + pType);
            /* We've done the necessary checks to show that this is safe. */
            @SuppressWarnings("unchecked")
            Class<? extends Creator<T>> creator = (Class<? extends Creator<T>>) any;
            return creator;
          }
        }
      }
    }
    throw new ClassCastException(fqcn + " does not implement Creator<String>");
  }

Основное ограничение, к которому вы должны присоединиться, состоит в том, что класс в иерархии должен указывать параметр типа. Например class MyCreator implements Creator<String>. Вы не можете использовать его с помощью class GenericCreator<T> implements Creator<T>.

В настоящее время он не обрабатывает действительный случай, когда вы создаете новый интерфейс interface StringCreatorIfc extends Creator<String>, и имеете реализацию класса. Это может быть усилено для этого, но я оставлю это как упражнение для тех, кто склонен.

Ответ 3

Вам не нужна эта строка. Вам также не нужен конструктор, потому что вы используете его по умолчанию. Просто создайте экземпляр класса непосредственно:

public static void main(String[] args) throws Exception {
        Class<?> someClass = Class.forName(args[0]);
        Creator<String> creator = (Creator<String>) someClass.newInstance();
}

Если вы настаиваете, вы сможете получить только половину:

public static void main(String[] args) throws Exception {
    Class<?> someClass = Class.forName(args[0]);
    Class<? extends Creator> creatorClass = someClass.asSubclass(Creator.class);
    Constructor<? extends Creator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
    Creator<String> creator = (Creator<String>) creatorCtor.newInstance((Object[]) null);
}

Ответ 4

Не совсем уверен, почему вы используете дженерики здесь.

Реализация объекта с использованием отражения предложила бы общее использование, но предположительно вы назовёте create в какой-то момент и присвойте результат String, иначе зачем использовать дженерики для управления типом возвращаемого значения.

Но если вы написали следующую реализацию Creator:

public class IntegerCreator implements Creator<Integer> 
{
  public Integer create() 
  { 
    ...
  }
}

И передал его как аргумент, вы получите ClassCastException при вызове create и назначении результата.