【问题标题】:Is static metaprogramming possible in Java?Java 中可以进行静态元编程吗?
【发布时间】:2010-09-11 20:34:00
【问题描述】:

我是static metaprogramming in C++ 的粉丝。我知道Java现在有泛型。这是否意味着在 Java 中可以进行静态元编程(即编译时程序执行)?如果是这样,任何人都可以推荐任何可以了解更多信息的好资源吗?

【问题讨论】:

  • 出于好奇:除了技术挑战之外,静态元编程还有哪些实际好处?
  • @Uri:将尽可能多的工作和错误源转移到编译时 - 代码生成、优化和静态多态性将是一些关键字。
  • 如果您可以具体化/典型化属性,您可以创建共享这些属性的表达式和结构,并且只有当它是/表现出您静态断言它是/执行的方式时才会编译。因此,您可以生成一个存在证明其正确性的程序。这只是一个开始。

标签: java metaprogramming


【解决方案1】:

不,这是不可能的。泛型不如模板强大。例如,模板参数可以是用户定义的类型、原始类型或值;但泛型模板参数只能是Object 或其子类型。

编辑:这是一个旧答案;自 2011 年以来,我们有了 Java 7,其中有 Annotations that can be used for such trickery

【讨论】:

  • 然而反射比模板更强大——为什么你会使用错误的工具(泛型)来完成这项工作?
  • 一个词:类型安全。 (零……一……看到了吗?一个字。)
  • @kravemir:代码生成或其他外部工具并不重要。注释可以,但在我写这篇文章时不存在:) 已编辑。
【解决方案2】:

看看Clojure。它是一个带有宏(元编程)的 LISP,在 JVM 上运行,并且与 Java 具有很强的互操作性。

【讨论】:

    【解决方案3】:

    简短的回答

    这个问题已经有将近 10 年的历史了,但我仍然缺少一个答案。这是:,但不是因为泛型和注意与 C++ 完全相同。

    从 Java 6 开始,我们有 the pluggable annotation processing api。静态元编程是(正如您在问题中已经说过的)

    编译时程序执行

    如果您了解元编程,那么您也知道这不是真的,但为了简单起见,我们将使用它。如果您想了解有关元编程的更多信息,请查看here

    可插入注解处理 api 由编译器在读取 .java 文件之后但在编译器将字节码写入 .class 文件之前调用。 (我有一个来源,但我再也找不到了。也许有人可以在这里帮助我?)。

    它允许您在编译时使用纯 java 代码执行逻辑。然而,你正在编码的世界是完全不同的。不是特别糟糕或什么,只是不同。您正在分析的类尚不存在,您正在处理这些类的元数据。但是编译器是在JVM中运行的,这意味着你也可以正常创建类和程序。但此外,您可以分析泛型,因为我们的注解处理器在 类型擦除之前调用。

    关于 java 中静态元编程的主要要点是,您提供元数据(以注释的形式),处理器将能够找到所有带注释的类来处理它们。 (更简单的)示例可以在Baeldung 上找到,其中形成了一个简单的示例。在我看来,这是一个很好的入门资源。如果您了解这一点,请尝试自己google。那里有很多好的资源,这里要列出很多。还可以查看Google AutoService,它利用注释处理器来消除您创建和维护服务文件的麻烦。如果你想创建类,我建议查看JavaPoet

    遗憾的是,这个 API 不允许我们操作源代码。但如果你真的想,你应该看看Project Lombok。他们这样做,但不受支持。


    为什么这很重要(感兴趣的人可以继续阅读)

    TL;DR:我很困惑,为什么我们不像动态那样使用静态元编程,因为它有很多优点。

    大多数开发人员看到“动态和静态”后立即得出结论,即动态更好。没有错,静态对开发人员有很多负面含义。但在这种情况下(特别是对于 java),情况恰恰相反。

    动态元编程需要反射,反射有somemajordrawbacks。他们有很多。简而言之:性能、安全性和设计。

    静态元编程(即注释处理)允许我们与编译器相交,编译器已经完成了我们尝试通过反射完成的大部分事情。我们还可以在这个过程中创建类,这些类再次传递给注释处理器。然后,您可以(例如)生成类,这些类通常必须使用反射来完成。此外,我们可以实现“快速失败”系统,因为我们可以将错误、警告等通知编译器。

    尽可能总结和比较:让我们想象一下春天。 Spring 尝试在运行时查找所有 Component 注释类(我们可以通过在编译时使用服务文件来简化它),然后生成某些代理类(我们已经可以在编译时完成)并解析 bean 依赖项(再次,我们已经可以在编译时完成)。 Jake Whartons talk about Dagger2,他在其中解释了为什么他们切换到静态元编程。我还是不明白为什么像Spring这样的大玩家不使用它。

    这篇文章很简短,以充分解释这些差异以及为什么静态会更强大。如果你愿意,我目前正在为此做一个演示。如果您有兴趣并会说德语(对此感到抱歉),您可以查看my website。在那里你会找到一个演示文稿,它试图在 45 分钟内解释这些差异。不过只有幻灯片。

    【讨论】:

      【解决方案4】:

      “静态元编程”到底是什么意思?是的,C++ 模板元编程在 Java 中是不可能的,但它提供了其他方法,比 C++ 中的方法强大得多:

      • 反思
      • 面向方面的编程 (@AspectJ)
      • 字节码操作(Javassist、ObjectWeb ASM、Java 代理)
      • 代码生成(注释处理工具,Velocity 等模板引擎)
      • 抽象语法树操作(流行 IDE 提供的 API)
      • 即使在运行时也可以运行 Java 编译器并使用已编译的代码

      没有最好的方法:每种方法都有其优点和缺点。 由于 JVM 的灵活性,Java 中的所有这些方法都可以在编译时和运行时使用。

      【讨论】:

      • 不,这不是“更强大”。这些机制中的每一个在 C++ 中也是可能的,但通常它由一些 C++ 模板库来解决,因此这些工具看起来不那么强大,但实际上它们并非如此。 (我看到很多 C++ 模板的语法和语义问题,但功能不足绝对不是其中之一。)
      • 在任何图灵完备的语言中一切皆有可能,但我们也在这里谈论易用性。例如,如果您正在编写(比方说)序列化或依赖注入或模拟库:在 Java 中,解决方案非常简单:只需使用反射并遍历类的字段。甚至不需要修改“数据”类。在 C++ 中,您必须修改“数据”类并使用一些预处理器宏,或者使用一些代码生成器。不像Java那样简单实用。
      • C++ 模板严格来说比 Java 泛型更强大,特别是如果您考虑易用性(如果您不这样做,功率比较无论如何都不再有意义)。你可以做原语,你可以做编译时计算,你可以为特定的实例添加专业化;对于 Java 泛型,这一切都是不可能的。 (请注意,我并不是说功率越大越好,那是另一种讨论。)
      • 现在对于其他工具和机制:您在那里进行了苹果与橙子的比较。类似的工具存在于 C++ - 显然不是字节码操作,但 AOP 确实存在(通过我相信的模板),代码生成是牢固的模板领域,因此实际上已经涵盖,AST 操作不算数,因为它不是 Java 而是 IDE(显然会对于 C++ 也同样存在),并运行完整的编译器 - 好吧,您在 C++ 领域使用 Lua,并且无论如何(通常)您都不希望在客户的机器上使用完整的 Java 编译器。
      【解决方案5】:

      没有。更重要的是,泛型类型会被编译器擦除到其上限,因此您无法在运行时创建泛型类型 T 的新实例。

      在 Java 中进行元编程的最佳方法是绕过类型擦除并提交类型 T 的 Class<T> 对象。不过,这只是一个 hack。

      【讨论】:

        【解决方案6】:

        如果您需要强大的 Java 编译时逻辑,一种方法是使用某种代码生成。因为,正如其他海报所指出的,Java 语言不提供任何适合执行编译时逻辑的功能,这可能是您的最佳选择(如果您确实需要编译时逻辑)。一旦你用尽了其他可能性并且你确定你想要进行代码生成,你可能会对我的开源项目 Rjava 感兴趣,可以在:

        http://www.github.com/blak3mill3r

        它是一个用 Ruby 编写的 Java 代码生成库,我编写它是为了自动为 Ruby on Rails 应用程序生成 Google Web Toolkit 接口。事实证明它非常方便。

        作为警告,调试 Rjava 代码可能非常困难,Rjava 不会做太多检查,它只是假设您知道自己在做什么。无论如何,这几乎就是静态元编程的状态。我想说,它比使用 C++ TMP 完成的任何重要操作都更容易调试,并且可以将它用于相同类型的事情。

        无论如何,如果您正在考虑编写一个输出 Java 源代码的程序,请立即停止并查看 Rjava。它可能还不能满足您的需求,但它已获得 MIT 许可,因此请随时对其进行改进、油炸或卖给您的祖母。我很高兴有其他具有泛型编程经验的开发人员对设计发表评论。

        【讨论】:

          【解决方案7】:

          Lombok 提供了一种弱形式的编译时元编程。但是,他们使用的技术是completely general

          相关讨论请见Java code transform at compile time

          【讨论】:

            【解决方案8】:

            Manifold project 为 Java 提供类型安全的静态元编程。当 Javac 解析类型名称时,您可以使用它在编译时动态构建类型。 JSON Schema demo 演示了动态编译时类型投影的静态元编程(无代码生成构建步骤)。

            此外,您还可以使用 Manifold 添加新功能。查看Structural TypingMethod ExtensionsType-safe Reflection。 .

            【讨论】:

            • 请添加关于您在项目中的角色的免责声明。谢谢。
            【解决方案9】:

            您可以使用用于 Java 的元编程库,例如 Spoon:https://github.com/INRIA/spoon/

            【讨论】:

              【解决方案10】:

              不,Java 中的泛型纯粹是一种避免对象转换的方法。

              【讨论】:

              【解决方案11】:

              【讨论】:

                猜你喜欢
                • 2010-11-24
                • 2010-09-19
                • 2012-01-13
                • 2013-06-06
                • 2019-11-20
                • 2016-05-19
                • 2013-07-19
                • 2010-09-25
                • 1970-01-01
                相关资源
                最近更新 更多