我是否打破了Java的局部变量类型推断?

时间:2018-12-12 15:29:09

标签: java type-inference

我写了一种方法,“典型化” String,并试图推断其中保存的数据类型。 (this gist的略微修改版本)。该方法在Map.Entry<Class, String>中返回推断的Class和原始String(可能稍作修改-包围空白的裁剪等)。例如,typify("3f")返回<Float, "3.0">typify(" c ")返回<Character, "c">,依此类推。

我的下一步是编写第二种方法,该方法“解码”这些返回的Map.Entry对象,以便可以将它们直接分配给推断类型的对象。例如:

Float f = decodeTypify(typify("3.14f"))
Boolean b = decodeTypify(typify("false"))

...等等。这段代码如下:

  @SuppressWarnings("unchecked")
  public static <T> T decodeTypify (Entry<Class, String> entry) {

    // String
    if (entry.getKey() == String.class)
      return (T) entry.getValue();

    // Boolean
    else if (entry.getKey() == Boolean.class)
      return (T) (Boolean) Boolean.parseBoolean(entry.getValue());

    // Byte
    else if (entry.getKey() == Byte.class)
      return (T) (Byte) Byte.parseByte(entry.getValue());

    // Character
    else if (entry.getKey() == Character.class)
      return (T) (Character) entry.getValue().charAt(0);

    // Short
    else if (entry.getKey() == Short.class)
      return (T) (Short) Short.parseShort(entry.getValue());

    // Integer
    else if (entry.getKey() == Integer.class)
      return (T) (Integer) Integer.parseInt(entry.getValue());

    // Long
    else if (entry.getKey() == Long.class)
      return (T) (Long) Long.parseLong(entry.getValue());

    // Float
    else if (entry.getKey() == Float.class)
      return (T) (Float) Float.parseFloat(entry.getValue());

    // Double
    else if (entry.getKey() == Double.class)
      return (T) (Double) Double.parseDouble(entry.getValue());

    // LocalDateTime
    else if (entry.getKey() == LocalDateTime.class)
      return (T) (LocalDateTime) stringAsDate(entry.getValue());

    else return null;
  }

这似乎很好用,特别是与Java的新局部变量类型推断结合使用时:

var f = decodeTypify(typify("literally anything"))

现在,我根本不需要关心返回的类型,因为Java会为f提供正确的类型。但是请注意,如果entry的{​​{1}}自变量的键与大decodeTypify()树中的任何选项都不匹配,则if-else返回{{1} }。这是在Java 11.0.1中的jshell中运行的方法:

decodeTypify()

我为本地类型推断变量分配了一个null值! isn't supposed to be possible。这种副作用(看来)是,我实际上可以告诉jshell> var x = decodeTypify(typify(null)) x ==> null 完全具有任何类型,而不会发出警告:

null

请注意,非x返回不是这种情况:

jshell> Object x = decodeTypify(typify(null))
x ==> null

jshell> String x = decodeTypify(typify(null))
x ==> null

jshell> Byte x = decodeTypify(typify(null))
x ==> null

我有事吗?如果没有,有人可以解释这是怎么回事吗?

2 个答案:

答案 0 :(得分:2)

您还没有打破任何东西。您不能直接分配null,但是可以通过方法调用间接分配null。

这样做的原因是,仅分配null即可使编译器没有信息来知道所需的类型。可以进行的唯一推断是针对可用的最通用类型Object,如果这是正确的推断,则只需将其明确声明为即可!还有3个额外的字符。

当编译器具有要使用的方法调用时,它可以使用方法的返回类型进行类型推断。

public static String foo() {
    return null;
}

public static <T> T bar() {
    return null;
}

public static <T> T baz(Class<T> clazz) {
    return null;
}

public static void main(String[] args) {
   var a = null;  // compile error
   var b = foo(); // fine
   var c = bar(); // fine
   var d = baz(String.class); //fine
}

答案 1 :(得分:0)

您可以将null值分配给类型推断的局部变量。 您只是不能使用null初始值设定项来实例化此类变量

我检查了您引用的要点,很明显,如果您在输入中给方法“ null”(值,而不是String),那么它将把对象设置为Object类型。 您正在初始化一个空对象,'var'可以处理。至少,编译器知道您正在使用Object类。此外,方法将具有返回类型,因此var也可以使用该返回类型。

关于类型分配切换副作用的东西... 广播null始终有效,因此,泛型能够很好地处理它并不奇怪:No Exception while type casting with a null in java