Получение T.class, несмотря на стирание типа Java

18

Я пытаюсь связать интерфейс с его реализацией как прочитанный из файла конфигурации, чтобы я мог передать его в мой контейнер IoC. Вот примерно то, что я пытаюсь сделать:

public class PropertyImplementationBinder<T> {
    // ...
    public Class getInterfaceClass() {
        return T.class; // OR Class<T>, note T is not newable
    }
    public Class getImplementationClass() {
        return /* read config file to get implementation class */;
    }
}

Как можно получить T.class ?     

задан Kaleb Pederson 09.02.2010 в 01:47
источник
  • этот кажется более элегантным –  jpfreire 28.01.2013 в 04:19

5 ответов

30

Вам нужно явно передать класс в конструктор (и сохранить его самостоятельно).

private final Class<T> clazz;

PropertyImplementationBinder(Class<T> clazz){
    this.clazz = clazz;
}

public Class<T> getInterfaceClass() {
    return clazz;
}
    
ответ дан Thilo 09.02.2010 в 01:51
источник
  • Я думаю, это должно было быть очевидно для меня ... но это не так. Спасибо! –  Kaleb Pederson 09.02.2010 в 06:12
  • Почему язык не может сделать это для нас? –  Pacerier 06.03.2012 в 12:45
  • Язык мог бы сделать это для нас, если бы он был разработан для него в версии 1. Это обратная совместимость. Было так много, что было бы полезно сделать, связав это с Java 5 без нарушения старого кода. –  Thilo 07.03.2012 в 01:11
  • Все смотрят ниже; во многих случаях можно обойтись без прохождения в классе. –  Andy 25.10.2013 в 17:46
8

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

  

Оказывается, что, хотя JVM не будет отслеживать фактические аргументы типа для экземпляров универсального класса, он отслеживает фактические аргументы типа для подклассов общих классов. Другими словами, хотя new ArrayList<String>() действительно является только new ArrayList() во время выполнения, если класс extends ArrayList<String> , то JVM знает, что String является фактическим аргументом типа для параметра типа List .

    
ответ дан kloffy 09.02.2010 в 02:01
источник
  • Но тогда разработчик тоже знает, так как класс имеет статическую T. Он не изменится во время выполнения. –  GuiSim 24.06.2014 в 16:26
5

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

Пожалуйста, взгляните на: Использование TypeTokens для извлечения общих параметров

В статье также рассказывается об опыте наших пользователей с этой техникой. В двух словах, мы закончили тем, что вернулись к ...

Обычный и широко используемый метод: «Пропустить типы классов в конструкторах»

    
ответ дан Richard Gomes 04.01.2011 в 02:53
источник
  • хорошо, это жутко, но удивительно (пусть это будет последний комментарий об увлечении). я действительно считаю, что это должен быть правильный ответ с замечанием «вы также можете передать класс в конструкторе» –  durilka 12.01.2012 в 23:26
  • @durilka: Большое спасибо за ваши отзывы –  Richard Gomes 07.07.2012 в 13:46
  • Ссылка кажется сломанной ... Я знаю, что библиотека GSON Google использует этот подход, и я хотел прочитать статью о том, как она работает –  Erin Drummond 22.12.2012 в 06:54
  • @ ErinDrummond исправлено со ссылкой на web.archive.org –  Eloff 23.01.2015 в 17:11
1

Btw. Пример статического метода getType в статье из @Richard Gomes имеет две ошибки. он должен выглядеть следующим образом:

static public Class<?> getType(final Class<?> klass, final int pos) {
    // obtain anonymous, if any, class for 'this' instance
    final Type superclass = klass.getGenericSuperclass();

    // test if an anonymous class was employed during the call
    if ( !(superclass instanceof ParameterizedType) ) {
            throw new RuntimeException("This instance should belong to an anonymous class");
    }

    // obtain RTTI of all generic parameters
    final Type[] types = ((ParameterizedType) superclass).getActualTypeArguments();

    // test if enough generic parameters were passed
    if ( pos >= types.length ) {
            throw new RuntimeException(String.format("Could not find generic parameter #%d because only %d parameters were passed", pos, types.length));
    }

    if (!(types[pos] instanceof Class<?>)) {
            throw new RuntimeException("Generic type is not a class but declaration definition(all you get is \"[T]\") " + types[pos]);
    }
    // return the type descriptor of the requested generic parameter
    return (Class<?>) types[pos];
}

К сожалению, это все еще не волшебная пуля, потому что она работает, если вы явно в коде

getType(new SomeObject<String>(){}.class, 0) // you get String.class

, но если вы назовете это чем-то вроде

getType(new SomeObject<T>(){}.class, 0) // you get T as TypeVariable<D> and not actuall class of it

Просто введите T.

    
ответ дан durilka 13.01.2012 в 02:00
источник
  • Во втором примере может оказаться возможным найти фактический параметр типа для T, если TypeVariable.getGenericDeclaration () является классом. В любом случае требуется много работы, чтобы дополнить фактические аргументы типа (особенно если они определены суперклассом, есть несколько параметров типа и т. Д., Но я написал код для этого. –  Andy 25.10.2013 в 17:50
-1

Нет, это невозможно.

Единственное исключение стирания стилей Java заключается в том, что через отражение вы можете узнать параметризованный тип через отражение в полях класса.

    
ответ дан cletus 09.02.2010 в 01:48
источник
  • Нет, вы также можете найти параметризованную информацию о типе из Class.getGeneric * () и Method.getGeneric * (). –  Andy 25.10.2013 в 17:45
  • Возможно видеть ответы выше. –  okigan 10.04.2014 в 19:17