Могу ли я использовать вложенные дженерики в Java?

Я пытался сделать что-то вроде:


public class MyClass <A, B, C <A, B> > {
  ...
}

Но Eclipse выделяет "B" и говорит "неожиданное, ожидаемое расширяется". Что дает? Разрешены ли вложенные дженерики?

6 ответов

Решение

Это потому, что вы не определили C как тип, который сам типизирован с 2 параметрами типа.
Попробуйте что-то вроде этого:

public class MyClass <A, B, C extends Map<A, B>> {
    // This compiles
}

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

Например:

interface IConverter<TFrom, TTo>
{
    TTo convert(TFrom from);
}

class IntToStringConverter implements IConverter<Integer, String>
{
    public String convert(Integer from)
    {
        return "This is a string: " + from.toString();
    }
}

class ConverterUser<TConverter extends IConverter<TFrom, TTo>, TFrom, TTo>
{
    public ConverterUser()
    {
    }

    private List<TConverter> _converter2;

    private TConverter _converter;

    public void replaceConverter(TConverter converter)
    {
        _converter = converter;
    }

    public TTo convert(TFrom from)
    {
        return _converter.convert(from);
    }
}

class Test
{
    public static void main(String[] args)
    {
        ConverterUser<IntToStringConverter, Integer, String> converterUser =
            new ConverterUser<IntToStringConverter, Integer, String>();

        converterUser.replaceConverter(new IntToStringConverter());

        System.out.println(converterUser.convert(328));
    }
}

Это невозможно в Java. См. Раздел " Переменные типа" определения языка, а также общие классы и параметры типа. Недавно я видел (где-то) упоминание о том, что Java не может этого сделать, но Scala может это сделать. Это подтверждается S4.4 Спецификации языка Scala.

Это также несколько подтверждается успешной компиляцией следующего кода.

class MyClass [A, B, C [A, B]] {
}

Компиляция в Java дала следующие ответы.

MyClass.java:1: > expected
class MyClass <A, B, C<A, B>> {
                      ^
MyClass.java:1: <identifier> expected
class MyClass <A, B, C<A, B>> {
                        ^
MyClass.java:1: ';' expected
class MyClass <A, B, C<A, B>> {
                           ^
MyClass.java:2: reached end of file while parsing
}
 ^
4 errors

Я думаю, что есть более легкое решение вашей проблемы, так как это несколько необычно.

Вам не нужно объявлять вложенные типы подобным образом. Просто

class MyClass<A, B, C> {}

И когда вы создаете MyClass, вы можете сделать что-то вроде

MyClass<List<String>, Set<Date>, Map<Integer, Long>> instance;

Я предполагаю, что вы хотите, чтобы MyClass был универсальным классом с параметрами типа A, B и C. Кроме того, вы хотите, чтобы C был универсальным классом с параметрами типа A и B.

Чтобы я мог написать

  1. MyClass < String , Date , Map < String , Date > >
  2. MyClass < String , Date , Hashtable < String , Date > >но нет
  3. MyClass < String , Date , ElementVsitor < Date , String > >

Тогда я не думаю, что вы можете сделать это.

Это эффективно требует типов более высокого порядка в Java. Java не поддерживает это напрямую, но его можно смоделировать несколько окольным путем, например так

      interface H<K, T> { }

Здесь Hкодирует тип более высокого порядка, который принимает параметр типа Kкоторый сам принимает параметр T.

Вы можете использовать это, например, для реализации универсального функтора. Обратите внимание, как fmapэффективно является функцией от H<K<T>>к H<K<R>>хотя мы не можем прямо заявить об этом таким образом.

      public interface Functor<K> {
    <T, R> Function<H<K, T>, H<K, R>> lift(Function<T, R> f);

    default <T, R> H<K, R> fmap(Function<T, R> f, H<K, T> h) {
        return lift(f).apply(h);
    }
}

См. также мой репозиторий Github для полных рабочих примеров. Кроме того, взгляните на этот вопрос , который еще больше продвигает концепцию.

Другие вопросы по тегам