【问题标题】:Functionnal programming in java and helpful compiler errorsJava 中的函数式编程和有用的编译器错误
【发布时间】:2022-12-17 23:15:38
【问题描述】:

我正在寻找一种方法来定义一段数据的通用计算,并在我弄错时让编译器大喊大叫。

来自打字稿世界,你可以这样做:

/**
 * Type inferred as:
 * Array<{
 *   a: number;
 *   b: string;
 *   c: { d: number; e: string };
 * }>
 */
const data = [
  { a: 1, b: "b", c: { d: 4, e: "e" } },
  { a: 2, b: "b", c: { d: 4, e: "e" } },
  { a: 3, b: "b", c: { d: 4, e: "e" } },
];

const result = data
  // change the type of c to string
  .map(o => ({...o, c: JSON.stringify(o.c)}))
  // adding a new field
  .map(o => ({...o, d: "new"}))
;
/** 
 * `result` type is automatically inferred as:
 * Array<{
 *     d: string;
 *     c: string;
 *     a: number;
 *     b: string;
 * }>
 */

最后,result 的类型将被正确推断,map 之间的每个类型也会被推断出来。

这里的好处是编译器实际上在帮助发现错误。

您将如何在 Java 中做类似的事情?

我尝试使用一个泛型 Map 对象,它的泛型类型跨越多个较低级别的类型,类似于 JS 字典。但是在这种情况下,编译器并不知道键 X 实际上是类型 Y,因此它在捕获错误方面完全没有用。

我的结论是,不可能在 Java 中进行与 Typescript 中相同类型的静态分析。 Java 编译器不是用来传播和更新未命名类型的,因为它会继续并捕获那些类型的错误。

【问题讨论】:

    标签: java generics compiler-errors static-analysis


    【解决方案1】:

    TypeScript(以及 JavaScript)在概念上与 Java 非常不同。

    在 TypeScript/JavaScript 中,一切都是围绕对象构建的,而在 Java 世界中,一切都是围绕类构建的。

    在 Java 中,你不能当场创建一个对象(就像可以在 TypeScript/JavaScript 中完成一样)。

    您需要先定义一个class(或record,它基本上是class 的一个特殊版本)。当然,您可以使用匿名内部类获取特定类型(如java.lang.Object)的子类型的实例,并且将在后台为您创建此子类,但您需要通用类型作为对象的属性,此选项不适合您的使用-情况(在创建匿名内部类时无法定义新的通用参数)。

    因此,您需要使用 classJava 16 record 来定义您的自定义泛型类型(请注意,记录是不可变的,即所有属性都是最终的).

    在您的示例中,与封闭对象 { a; b; c; } 的结构相匹配的通用类可能如下所示:

    public class MyType<A, B, C> {
        private A a;
        private B b;
        private C c;
        
        // constructor, getters, etc.
    }
    

    具有{ d; e; }结构的内部对象可以描述为以下记录(请注意,记录的规范全参数构造函数、吸气剂、equals/hashCodetoString 将由编译器自动生成,因此记录定义实际上可以是一行右):

    public record Pair<L, R>(L left, R right) {}
    

    笔记:Vavr 和 Apache Commons 等库提供 TriplePair 类型,但它们在 JDK 中不可用。有些人可能会说标准 JDK 的 Map.EntryPair 的模糊类似,在语义上会被用来用映射条目替换域类型。

    关于类型推断,在Java中我们有Target type这样的概念,聚合表达式(上下文相关的表达式,应该从赋值上下文、调用上下文或转换上下文中推断出哪种类型)等。这个主题非常广泛,可能会遇到与类型推断相关的许多问题,但简单来说,如果您需要编译器推断某些类型,应该有一些信息来源(如赋值上下文、参数类型等)。

    这是使用 Java 10 Local variable type inference via var word 和 Java 7 diamond &lt;&gt; operator 进行类型推断的示例(假设 MyType 声明了无参数和全参数构造函数):

    MyType<Integer, String, Pair<Integer, String>> myType = new MyType<>();
            
    var myType2 = new MyType<>(1, "b", new Pair<>(4, "e")); // <> literally means - infer the type of generic parameters
    

    【讨论】:

      猜你喜欢
      • 2014-05-09
      • 1970-01-01
      • 2012-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-10
      • 1970-01-01
      相关资源
      最近更新 更多