Могу ли я использовать вложенные дженерики в 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.
Чтобы я мог написать
MyClass < String , Date , Map < String , Date > >
MyClass < String , Date , Hashtable < String , Date > >
но нет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 для полных рабочих примеров. Кроме того, взгляните на этот вопрос , который еще больше продвигает концепцию.