Я нарушил вывод типа локальной переменной Java? - PullRequest
0 голосов
/ 12 декабря 2018

Я написал метод, который "типизирует" String и пытается определить тип данных, которые в нем хранятся.(Немного измененная версия этой сути ).Метод возвращает логический Class и исходный String (возможно, слегка измененный - окружающий пробел, обрезанный и т. Д.) В Map.Entry<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 для decodeTypify() имеет ключ, который не соответствует ни одному из параметров в большом дереве if-else, то decodeTypify() возвращает null.Вот этот метод, выполняющийся в jshell с Java 11.0.1:

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

Обратите внимание, что это не относится к не- null возвращает:

jshell> var x = decodeTypify(typify("3"))
x ==> 3.0

jshell> Boolean x = decodeTypify(typify("3"))
|  Exception java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.Boolean (java.lang.Double and java.lang.Boolean are in module java.base of loader 'bootstrap')
|        at (#21:1)

Я что-то сломал?Если нет, может кто-нибудь объяснить, что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Вы можете присвоить нулевое значение локальной переменной с выводом типа. Вы просто не можете создать экземпляр такой переменной с нулевым инициализатором .

Я проверил ту суть, на которую вы ссылались, и ясно, что если вы передадите методу 'null', как при вводе (значение, а не String), тогда он будет устанавливать тип объекта. Вы инициализируете нулевой объект , что-то, что может обработать var.По крайней мере, компилятор знает, что вы работаете с классом Object.Кроме того, метод будет иметь тип возвращаемого значения, поэтому var может работать и с этим.

Что касается побочных эффектов переключения назначения типа ... приведение к нулю всегда работает , поэтомуне странно, что дженерики справляются с этим просто отлично: Без исключений при приведении типа с нулем в java

0 голосов
/ 12 декабря 2018

Вы ничего не сломали.Вы не можете назначить 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
}
...