Java中更高级的泛型

时间:2009-05-18 09:50:51

标签: java generics haskell polymorphism higher-kinded-types

假设我有以下课程:

public class FixExpr {
  Expr<FixExpr> in;
}

现在我想介绍一个泛型参数,抽象使用Expr:

public class Fix<F> {
  F<Fix<F>> in;
}

但Eclipse并不喜欢这样:

  

F型不是通用的;它不能使用参数&lt; Fix&lt; F&gt;&gt;

进行参数化

这是可能的还是我忽略了导致这个特定实例破坏的东西?

一些背景信息:在Haskell中,这是编写泛型函数的常用方法;我正在尝试将其移植到Java。上例中的类型参数F具有种类* - &gt; *而不是通常的种类*。在Haskell中它看起来像这样:

newtype Fix f = In { out :: f (Fix f) }

6 个答案:

答案 0 :(得分:25)

也许您可以尝试Scala,它是一种在JVM上运行的函数式语言,支持更高级的泛型。


[编辑Rahul G]

以下是您的特定示例粗略转换为Scala的方式:

trait Expr[+A]

trait FixExpr {
  val in: Expr[FixExpr]
}

trait Fix[F[_]] {
  val in: F[Fix[F]]
}

答案 1 :(得分:24)

我认为您尝试做的事情根本不受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,因此它拒绝编译。

答案 2 :(得分:5)

为了传递一个类型参数,类型定义必须声明它接受一个(它必须是泛型)。显然,您的F不是通用类型。

更新:行

F<Fix<F>> in;

声明一个类型F的变量,它接受一个类型参数,其值为Fix,它本身接受一个类型参数,其值为F。您的示例中甚至没有定义F。我认为你可能希望

Fix<F> in;

这将为您提供一个类型Fix的变量(您在示例中定义的类型),您将向其传递值为F的类型参数。由于Fix被定义为接受类型参数,因此可以使用。

更新2:重新阅读您的标题,现在我认为您可能正尝试执行与"Towards Equal Rights for Higher-Kinded Types"中提供的方法类似的操作(PDF警报)。如果是这样,Java不支持,但您可以尝试Scala。

答案 3 :(得分:1)

但是,有一些方法可以用Java编码高质量的泛型。请看higher-kinded-java project

将此作为库使用,您可以像这样修改代码:

public class Fix<F extends Type.Constructor> {
    Type.App<F, Fix<F>> in;
}

您可能应该在@GenerateTypeConstructor

中添加Expr注释
@GenerateTypeConstructor
public class Expr<S> {
    // ...
}

此注释生成ExprTypeConstructor类。 现在您可以像这样处理您的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);
        }
    }

}

答案 4 :(得分:1)

正如 Victor 所指出的,有一种迂回的方式在 Java 中编码更高级的类型。它的要点是引入一个类型 <!--Bottom Section--> <section class="row"> <!--First Row--> <!--Card One--> <div class="col-lg-3"> <div class="card"> <img class="img-fluid" src="https://placebear.com/225/225"/> <p class="card-body">These bears are playing around</p> <button class="card-body bg-primary p-lg-2 m-2 rounded text-light">See more bears</button> </div> </div> <!--Card Two--> <div class="col-lg-3"> <div class="card"> <img class="img-fluid" src="https://placebear.com/225/224"/> <p class="card-body">This bear is looking off into the distance</p> <button class="card-body bg-primary p-lg-2 m-2 rounded text-light">See more bears</button> </div> </div> <!--Second Row--> <!--Card Three--> <div class="col-lg-3"> <div class="card"> <img class="img-fluid" src="https://placebear.com/225/223"/> <p class="card-body">This bear just found some food</p> <button class="card-body bg-primary p-lg-2 m-2 rounded text-light">See more bears</button> </div> </div> <!--Card Four--> <div class="col-lg-3"> <div class="card"> <img class="img-fluid" src="https://placebear.com/225/222"/> <p class="card-body">This bear is one of our favorites</p> <button class="card-body bg-primary p-lg-2 m-2 rounded text-light">See more bears</button> </div> </div> </section> 来编码 H<F, T>。然后可以使用它来编码函子的不动点(即 Haskell 的 F<T> 类型):

Fix

从这里开始,您可以继续在初始代数上实现变形:

public interface Functor<F, T> {
    <R> H<F, R> map(Function<T, R> f);
}

public static record Fix<F extends H<F, T> & Functor<F, T>, T>(F f) {
    public Functor<F, Fix<F, T>> unfix() {
        return (Functor<F, Fix<F, T>>) f;
    }
}

请参阅我的 GitHub repo 以获取工作代码,包括一些示例代数。 (请注意,IDE 就像 IntelliJ 一样,尽管它在 Java 15 上编译和运行都很好),但它在代码方面很挣扎。

答案 5 :(得分:0)

看起来你可能想要这样的东西:

public class Fix<F extends Fix<F>> {
    private F in;
}

(参见Enum类,以及有关其泛型的问题。)