Конфликты Generics и Method Generics класса Java - PullRequest
2 голосов
/ 20 апреля 2011

У меня есть класс Generic Class Factory, у которого есть два метода: один использует универсальное значение T класса, а другой использует только собственные определения общих методов.

public class GenericClassFactory<T extends ClassMatchable> {
    public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
    public <K> T newObject(K key, String packageName){...}
}

Метод, который использует универсальный T, работает нормально, но когда я хочу использовать другой метод, который не заботится о том, что является универсальным T, он не будет использовать Generic E, он просто возвратит Object, а затем наберите его.

Data data = new GenericClassFactory().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

Это приводит к ошибкам компиляции, потому что он хочет, чтобы я ввел его (Data). Если я передам GenericClassFactory действительный класс Generic, он будет работать. Это похоже на то, что он не распознает обобщенные методы, если у вас есть определенный обобщенный класс, но он не используется.

Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

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

public class GenericClassFactory {
    public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
    public <T extends ClassMatchable, K> T newObject(K key, String packageName){...}
}

Но теперь мой второй метод кажется слишком широким или что-то в этом роде ... возможно, нет. Я имею в виду, что он все равно выдаст ошибку компиляции, если объект, который вы назначаете для возвращаемого типа, не реализует ClassMatchable. Это путь, которым я должен идти? Чтобы мне не приходилось печатать?

Ответы [ 4 ]

6 голосов
/ 20 апреля 2011

Правильно, если вы не введете ссылку на класс, то даже обобщенные методы, которые используют только параметры типа метода, не будут генерироваться. Это один из странных нюансов Java Generics. Как вы говорите, вы можете указать произвольный тип для T:

Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

Но, скорее всего, это даже не метод экземпляра. Разве это не может быть статическим методом? Если это так, вы можете просто вызвать его так:

Data data =  GenericClassFactory.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

Редактировать

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

public class Scratchpad<T> {
   List<String> list;
   public static void main(String[] args) {
      Scratchpad sp = new Scratchpad();
      List<Integer> list = sp.list;
   }
}

И это потому, что sp.list разрешается как List, а не List<String>, , хотя Scratchpad.list не имеет ничего общего с T.

Это подробно задокументировано в JLS, Раздел 4.8 :

Тип конструктора (§8.8), метод экземпляра (§8.8, §9.4) или нестатическое поле (§8.3) M необработанного типа C, который не унаследован от его суперклассы или суперинтерфейсы - это удаление его типа в обобщенном объявлении, соответствующем C. Тип статического члена необработанного типа C такой же, как его тип в обобщенном объявлении, соответствующем C.

1 голос
/ 20 апреля 2011

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

Совершенно верно. Если вы определяете общее ограничение для класса, а затем создаете экземпляр класса без предоставления какого-либо общего ограничения (то есть вы полностью исключаете <>), то вы только что вступили в область Необработанные типы , где больше ничего не остается.

Необработанные типы существуют только для обратной совместимости. По словам Анжелики Лангер: «1013 *,

, часто задаваемые вопросы по Java Generics»

Использование необработанных типов в коде, написанном после введения универсальности в язык программирования Java, не рекомендуется. Согласно Спецификации языка Java, возможно, что будущие версии языка программирования Java будут запрещать использование необработанных типов.

Это также состояния :

Методы или конструкторы необработанного типа имеют сигнатуру, которую они имели бы после стирания типа. При вызове метода или конструктора необработанного типа генерируется непроверенное предупреждение, если стирание изменяет типы аргументов.

Если метод newObject() не использует параметр типа T класса, к которому он принадлежит, значит, с вашим дизайном что-то не так: скорее всего newObject() следует сделать статическим методом.

Однако, если по какой-то причине это действительно должен быть метод экземпляра, вы можете использовать его с помощью подстановочного типа GenericClassFactory<?>:

GenericClassFactory<?> gcf = new GenericClassFactory();
Data data = gcf.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl"); 
1 голос
/ 20 апреля 2011

Вы должны указать фактические типы E и K при вызове метода:

  new GenericClassFactory<ClassMatchable>().<TypeforE, TypeforK>newObject(...)

Похоже, что Java не может вывести его из аргумента.

И, конечно,:

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

точно правильный.

0 голосов
/ 20 апреля 2011

Рассмотрим, действительно ли T является параметром типа класса или метода.Например, есть ли в классе что-то, что ограничивает созданные типы T (на уровне класса), или, возможно, это используется для удобства, чтобы избежать приведения значений результата (на уровне метода).выложили здесь, мне кажется, что T должен быть типом метода, и что ваш последний пример является ответом.Определение метода не кажется слишком широким, если реализация класса является универсальной и может выдавать разные типы при каждом вызове метода.

...