本地类型推断与实例

Local Type Inference vs Instance

我已经尝试浏览 JEP-286 关于局部类型推断的内容。我看到这仅适用于局部变量 - 理解。所以这确实有效:

public class TestClass {
    public static void main(String [] args){
        var list = new ArrayList<>();
        list.add("1");
        System.out.println(list.get(0)); // 1
    }  
}

我确实看到另一方面无法编译:

public class TestClass {
    public var list = new ArrayList<>();
    public static void main(String [] args){

    }
}

显然不是,因为 JEP 是这么说的。现在我的问题:

声明为 varpublic/protected 成员失败是完全合理的,至少在 IMO 中是这样。但是为什么即使它是 private 也不能编译?我只能假设您仍然可以通过反射获得该变量(并且我无法获得这样的本地字段)......并且获得该变量需要一个演员,好吧,可能是一个非常混乱的演员。

允许 var 用于私有字段 (IMO) 是一个合理的决定。但省略它会使功能更简单。

在对 local-only 类型推断有更多经验后,也可以在将来的某个版本中添加它,而删除功能要困难得多。

各种原因:

  1. 可见性和类型是正交的 - 一个不应影响另一个。如果可以使用 var 初始化私有变量,则必须在将它们设置为受保护或 public.

  2. 时更改它
  3. 因为var使用right-hand端来推断类型,所以这样的私有字段总是需要立即初始化。如果将初始化移动到构造函数中,则必须明确类型。

  4. 使用 var 编译器可以推断出您目前无法在 Java 中表达的类型(例如 Comparable & Serializable 等交集类型)。你当然可能最终依赖于那些特定类型,当你出于任何原因不得不在某个时候停止使用 var 时,你可能不得不进行大量重构以保持你的代码正常工作。

详细说明 (特别是他的第二个原因),JLS 10 的拟议草案指出 var e;var g = null; 对于局部变量都是非法的,并且有充分的理由;从 right-hand 方面(或缺少方面)尚不清楚要为 var.

推断哪种类型

目前,non-final 实例变量会根据它们的类型自动初始化(0false 的原语,以及 null 的引用,我敢肯定你已经知道了)。实例变量的推断类型将保持不清楚,除非它在声明时或在其各自的 class' 构造函数中初始化。

出于这个原因,我支持仅在变量同时为 privatefinal 时才允许使用 var,这样我们可以确保在class 已创建。不过,我不能说这有多难实现。

将这些变量变成可以通过反射检查的字段并不是完全不可能的。例如,你可以做

var l = new ArrayList<String>();
l.add("text");
System.out.println(l);
System.out.println(
  new Object(){ { var x = l; } }.getClass().getDeclaredFields()[0].getGenericType()
);

在当前版本中,它只是打印ArrayList,所以实际的泛型类型还没有存储在匿名内部class的class文件中,这不太可能会改变,因为支持这种内省不是实际目标。这也只是一种特殊情况,类型可以表示为 ArrayList<String>。为了说明不同的情况:

var acs = true? new StringBuilder(): CharBuffer.allocate(10);
acs.append("text");
acs.subSequence(1, 2);
System.out.println(
  new Object(){ { var x = acs; } }.getClass().getDeclaredFields()[0].getGenericType()
);

acs的类型是AppendableCharSequence的交集类型,调用其中任一接口的方法即可证明,但由于未指定是否编译器推断 #1 extends Appendable&CharSequence#1 extends CharSequence&Appendable,未指定代码是否打印 java.lang.Appendablejava.lang.CharSequence.

我认为这不是合成字段的问题,但对于显式声明的字段,可能是。

但是,我怀疑专家组是否详细考虑了这些影响。相反,不支持字段声明的决定(因此跳过了对含义的冗长思考)是从一开始就做出的,因为局部变量始终是该功能的预期目标。局部变量的数量远高于字段声明的数量,因此减少局部变量声明的样板文件影响最大。

禁止对字段和方法 returns 进行类型推断的动机是 APIs 应该是稳定的;字段访问和方法调用在运行时由描述符链接,因此如果对实现的更改导致推断类型发生更改(模擦除),则导致推断类型发生细微变化的事情可能会导致现有编译客户端以可怕的方式中断。因此使用这是为了实施,而不是为了 API,是一个明智的指导原则。

问 "so, what about private fields and methods?" 是合理的,事实上,我们完全可以选择这样做。像所有设计决策一样,这是一种权衡;它将使推理能够在更多地方使用,以换取用户模型的更多复杂性。 (我不太关心规范或编译器的复杂性;那是我们的问题。)推理 "inference for local variables yes, fields and methods no" 比添加诸如 "but, fields and methods are OK if they are private" 的各种周转考虑更容易。在我们所做的地方画线也意味着将字段或方法从私有更改为非私有的兼容性结果不会与推理发生意外交互。

所以简短的回答是,这样做可以使语言更简单,而不会显着降低该功能的用处。