Общий класс Java - определение типа - PullRequest
49 голосов
/ 17 июня 2009

Если я создаю общий класс Java, например:

public class Foo<T>

Как можно внутренне определить для этого класса, что такое «Т»?

public ???? Bar()
{
    //if its type 1
    //    do this
    //if its type 2
    //    do this
    //if its type 3
    //    do this
    //if its type 4
    //    do this
}

Я изучил Java API и поиграл с такими вещами, как Reflection, instanceof, getClass, .class и т. Д., Но мне кажется, что я не вижу их в этом. Я чувствую, что я рядом, и мне просто нужно объединить несколько звонков, но продолжаю набирать обороты.

Чтобы быть более конкретным, я пытаюсь определить, был ли создан экземпляр класса с одним из 3 возможных типов.

Ответы [ 8 ]

58 голосов
/ 17 июня 2009

Я использовал решение, подобное тому, что он объясняет здесь для нескольких проектов, и нашел его довольно полезным.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

Суть его в следующем:

 public Class returnedClass() {
     ParameterizedType parameterizedType = (ParameterizedType)getClass()
                                                 .getGenericSuperclass();
     return (Class) parameterizedType.getActualTypeArguments()[0];
}
44 голосов
/ 17 июня 2009

В отличие от .NET Java, дженерики реализованы методом, называемым «стирание типа».

Это означает, что компилятор будет использовать информацию о типе при генерации файлов классов, но не будет передавать эту информацию в байт-код. Если вы посмотрите на скомпилированные классы с помощью javap или подобных инструментов, вы обнаружите, что List<String> - это простой List (из Object) в файле классов, как это было в коде до Java-5.

Код, обращающийся к универсальному списку, будет «переписан» компилятором для включения приведений, которые вы должны были бы написать самостоятельно в более ранних версиях. По сути, следующие два фрагмента кода идентичны с точки зрения байтового кода после того, как с ними закончил компилятор:

Java 5:

List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);

Java 1.4 и ранее:

List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);

При чтении значений из универсального класса в Java 5 автоматически вставляется необходимое приведение к объявленному параметру типа. При вставке компилятор проверит значение, которое вы пытаетесь ввести, и прервет его с ошибкой, если это не строка.

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

У обоих подходов есть свои плюсы и минусы, но так обстоит дело в Java.

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

12 голосов
/ 17 июня 2009

Из-за стирания типа нет способа сделать это напрямую. Тем не менее, вы можете передать Class<T> в конструктор и удерживать его внутри своего класса. Затем вы можете проверить его по трем возможным Class типам, которые вы разрешаете.

Однако, если есть только три возможных типа, вы можете рассмотреть возможность рефакторинга в enum .

3 голосов
/ 17 июня 2009

Проблема в том, что большая часть общего содержимого исчезнет во время компиляции.

Одним из распространенных решений является сохранение типа во время создания объекта.

Для краткого ознакомления с поведением java типа Erasure прочитайте это page

1 голос
/ 07 января 2013

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

Так

public class Foo<T>

public ???? Bar()
{
    //else condition goes here
}

А потом

public class DateFoo extends Foo<Date>

public ???? Bar()
{
    //Whatever you would have put in if(T == Date) would go here.
}
0 голосов
/ 17 июня 2009

Я согласен с Visage. Обобщение предназначено для проверки во время компиляции, а не для динамической типизации во время выполнения. Похоже, что вам нужно на самом деле просто заводской шаблон. Но если ваше «сделать это» не является экземпляром, тогда простой Enum, вероятно, будет работать так же хорошо. Как и то, что сказал Майкл, если у вас есть немного более конкретный пример, вы получите лучшие ответы.

0 голосов
/ 17 июня 2009

Похоже, что вы на самом деле не класс Generic, а интерфейс с множеством различных реализаций. Но, возможно, станет яснее, если вы сформулируете свою конкретную конкретную цель.

0 голосов
/ 17 июня 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...