Высшие сородичи в Java
Предположим, что у меня есть следующий класс:
public class FixExpr {
Expr<FixExpr> in;
}
Теперь я хочу ввести общий аргумент, абстрагироваться от использования Expr:
public class Fix<F> {
F<Fix<F>> in;
}
Но Eclipse не нравится:
Тип F не является общим; он не может быть параметризован с помощью аргументов < Fix <F →
Возможно ли это вообще или я пропустил что-то, что заставляет этот конкретный экземпляр сломаться?
Некоторая справочная информация: в Haskell это обычный способ записи общих функций; Я пытаюсь передать это на Java. Аргумент типа F в приведенном выше примере имеет вид * → * вместо обычного вида *. В Haskell это выглядит так:
newtype Fix f = In { out :: f (Fix f) }
Ответы
Ответ 1
Я думаю, что то, что вы пытаетесь сделать, просто не поддерживается дженериками Java. Простейший случай
public class Foo<T> {
public T<String> bar() { return null; }
}
также не компилируется с помощью javac.
Так как Java не знает во время компиляции, что T
, он не может гарантировать, что T<String>
имеет смысл. Например, если вы создали Foo<BufferedImage>
, bar
будет иметь подпись
public BufferedImage<String> bar()
что является бессмысленным. Поскольку нет механизма заставить вас создавать экземпляр Foo
с общим T
s, он отказывается компилировать.
Ответ 2
Возможно, вы можете попробовать Scala, который является функциональным языком, запущенным на JVM, который поддерживает более общие генераторы.
[EDIT by Rahul G]
Вот как ваш конкретный пример примерно переводится на Scala:
trait Expr[+A]
trait FixExpr {
val in: Expr[FixExpr]
}
trait Fix[F[_]] {
val in: F[Fix[F]]
}
Ответ 3
Чтобы передать параметр типа, определение типа должно объявить, что оно принимает одно (оно должно быть общим). По-видимому, ваш F
не является общим типом.
UPDATE: строка
F<Fix<F>> in;
объявляет переменную типа F
, которая принимает параметр типа, значение которого равно Fix
, которое само принимает параметр типа, значение которого равно F
. F
даже не определен в вашем примере. Я думаю, вы можете хотеть
Fix<F> in;
Это даст вам переменную типа Fix
(тип, который вы определили в своем примере), к которому вы передаете параметр типа со значением F
. Поскольку Fix
определяется для принятия параметра типа, это работает.
ОБНОВЛЕНИЕ 2: перечитайте свой заголовок, и теперь я думаю, что вы, возможно, пытаетесь сделать что-то похожее на подход, представленный в "На пути к равным правам для более высоко- Kinded Types" (уведомление в формате PDF). Если это так, Java не поддерживает это, но вы можете попробовать Scala.
Ответ 4
Тем не менее, есть способы кодировать более обобщенные генераторы в Java. Пожалуйста, посмотрите проект с более высоким качеством java.
Используя это как библиотеку, вы можете изменить свой код следующим образом:
public class Fix<F extends Type.Constructor> {
Type.App<F, Fix<F>> in;
}
Вероятно, вы должны добавить аннотацию @GenerateTypeConstructor
к вашему классу Expr
@GenerateTypeConstructor
public class Expr<S> {
// ...
}
Эта аннотация генерирует класс ExprTypeConstructor.
Теперь вы можете обработать Fix Expr следующим образом:
class Main {
void run() {
runWithTyConstr(ExprTypeConstructor.get);
}
<E extends Type.Constructor> void runWithTyConstr(ExprTypeConstructor.Is<E> tyConstrKnowledge) {
Expr<Fix<E>> one = Expr.lit(1);
Expr<Fix<E>> two = Expr.lit(2);
// convertToTypeApp method is generated by annotation processor
Type.App<E, Fix<E>> oneAsTyApp = tyConstrKnowledge.convertToTypeApp(one);
Type.App<E, Fix<E>> twoAsTyApp = tyConstrKnowledge.convertToTypeApp(two);
Fix<E> oneFix = new Fix<>(oneAsTyApp);
Fix<E> twoFix = new Fix<>(twoAsTyApp);
Expr<Fix<E>> addition = Expr.add(oneFix, twoFix);
process(addition, tyConstrKnowledge);
}
<E extends Type.Constructor> void process(
Fix<E> fixedPoint,
ExprTypeConstructor.Is<E> tyConstrKnowledge) {
Type.App<E, Fix<E>> inTyApp = fixedPoint.getIn();
// convertToExpr method is generated by annotation processor
Expr<Fix<E>> in = tyConstrKnowledge.convertToExpr(inTyApp);
for (Fix<E> subExpr: in.getSubExpressions()) {
process(subExpr, tyConstrKnowledge);
}
}
}
Ответ 5
Похоже, вам может понадобиться что-то вроде:
public class Fix<F extends Fix<F>> {
private F in;
}
(См. класс Enum и вопросы о его дженериках.)