本地类型推断与实例

时间:2018-03-02 13:46:09

标签: type-inference java-10

我试图扫描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 public / protected 成员失败,至少是IMO,这是完全合理的。但是为什么即使它是private也不能编译?我只能假设你仍然可以通过反射获得该变量(我不能得到这样的局部字段)......并且获得该变量需要一个强制转换,好吧,这可能是一个非常困惑的演员。

5 个答案:

答案 0 :(得分:14)

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

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

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

答案 1 :(得分:5)

各种原因:

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

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

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

答案 2 :(得分:5)

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

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()
);

acs的类型是AppendableCharSequence的交集类型,通过调用其上的任一接口的方法来证明,但是因为没有指定编译器是否推断#1 extends Appendable&CharSequence#1 extends CharSequence&Appendable,未指明代码是打印java.lang.Appendable还是java.lang.CharSequence

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

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

答案 3 :(得分:3)

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

此外,在有更多使用本地类型推断的经验后,可以在以后的某个版本中添加它,而删除某个功能要困难得多。

答案 4 :(得分:3)

关于Nicolai's answer(特别是他的#2理由)的阐述,JLS 10的拟议草案指出var e;var g = null;对于局部变量都是非法的,并且有充分的理由;从右侧(或缺少)不清楚哪种类型可以推断为var

目前,非最终实例变量会根据其类型(0false的原语以及null的引用自动初始化,因为我确定您已经知道)。实例变量的推断类型将保持不清楚,除非它在声明或其各自的类中初始化。构造(多个)。

出于这个原因,我支持仅在变量同时为varprivate时才允许final使用,因此我们可以确保在该类的时间初始化它创建。但是,我不能说实施起来会有多困难。