8

我试图扫描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 是这么说的。现在我的问题:

对于宣布失败的公共/受保护var成员(至少 IMO)来说,这是完全合理的。但是为什么即使它是它也不能编译private?我只能假设您仍然可以通过反射获得该变量(并且我无法获得这样的本地字段)......并且获得该变量将需要一个演员,好吧,可能是一个非常混乱的演员。

4

5 回答 5

20

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

有理由问“那么,私有字段和方法呢?” 事实上,我们很可能会选择这样做。像所有设计决策一样,这是一个权衡;它将使推理能够在更多地方使用,以换取用户模型的更多复杂性。(我不太关心规范或编译器的复杂性;那是我们的问题。)推理“局部变量是,字段和方法否”比添加各种周转考虑因素(例如“但是,字段”)更容易推理如果它们是私有的,则方法是可以的”。在我们所做的地方画线也意味着将字段或方法从私有更改为非私有的兼容性后果不会与推理发生意外交互。

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

于 2018-03-02T20:59:09.767 回答
6

很多原因:

  1. 可见性和类型是正交的——一个不应该影响另一个。如果私有变量可以用 初始化var,那么在将它们设为受保护或公开时,您必须更改它。

  2. 因为var使用右手边来推断类型,所以这些私有字段总是需要立即初始化。如果将初始化移动到构造函数中,则必须使类型显式。

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

于 2018-03-02T14:27:12.883 回答
6

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

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,所以实际的泛型类型并没有存储在匿名内部类的类文件中,这不太可能改变,因为支持这种内省并不是真正的目标。这也只是类型可表示为的一种特殊情况,如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()
);

的类型是andacs的交集类型,如通过调用任一接口的方法所证明的那样,但由于未指定编译器是否推断or ,因此未指定代码是否将打印or 。AppendableCharSequence#1 extends Appendable&CharSequence#1 extends CharSequence&Appendablejava.lang.Appendablejava.lang.CharSequence

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

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

于 2018-03-02T16:18:02.583 回答
5

详细阐述Nicolai 的回答(特别是他的#2 原因),JLS 10 的拟议草案指出,对于局部变量,两者都是非法的,var e;并且var g = null;有充分的理由;从右侧(或缺少右侧)不清楚要推断出哪种类型var.

目前,非最终实例变量会根据它们的类型自动初始化(和 的原语0false以及对 的引用null,我相信你已经知道了)。实例变量的推断类型仍不清楚,除非它在声明时或在其各自类的构造函数中初始化。

出于这个原因,我支持var只允许在变量为 both 时使用privatefinal因此我们可以确保在创建类时初始化它。不过,我不能说这将是多么难以实施。

于 2018-03-02T16:08:28.710 回答
4

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

此外,在对仅本地类型推断有更多经验之后,可以在将来的某个版本中添加它,而删除一个特性要困难得多。

于 2018-03-02T14:16:33.830 回答