Преобразование из строки в универсальный тип

#java #generics #casting

#java #универсальные #Кастинг

Вопрос:

Мне нужно преобразовать строку в значение переменной. Я нашел решение только для C #. Мне это нужно на Java.

 public class Property<T> {

    T value;

    public void setValue(String input){
        if(value instanceof String){
           value= input; // value is type of T not type of string (compilation error)
                         // incompatible types: String cannot be converted to T
        }
        if(value instanceof int){
           //parse string
        }
        if(value instanceof boolean){
           //parse string
        }
        ...
    }
}
  

Комментарии:

1. если вы не знаете, что такое T, вы не сможете преобразовать в него. Вы можете выполнить приведение, но это может легко привести к сбою. Плохой дизайн, я бы сказал.

2. Что такое «определенный тип универсальной переменной»?

3. Это не имеет смысла, как это может не быть String , если аргумент имеет тип string.

4. Я имею в виду тип T, который я распознаю с помощью instanceof

5. @SpeedEX505 Не буду врать, я нашел это методом проб и ошибок. Для меня это тоже очень непонятно. Но я не возражаю против грязных трюков, пока они выполняют мои требования xD

Ответ №1:

Это не так, как это работает. Однако вы можете использовать полиморфизм для достижения полезного результата.

Решение с полиморфизмом

Базовое универсальное (и абстрактное) свойство

 public abstract class Property<T> {
    T value;
    public abstract void setValue(String input);
}
  

Свойство для строк

 public class StringProperty extends Property<String> {
    @Override
    public void setValue(String input) {
        this.value = input;
    }
}
  

Свойство для целых чисел

 public class IntegerProperty extends Property<Integer> {
    @Override
    public void setValue(String input) {
        this.value = Integer.valueOf(input);
    }
}
  

Не уверен, какова ваша фактическая цель, но этот подход может сработать.

Обратите внимание, что input instanceof T произойдет сбой из-за удаления типа. Это не сработает.


Решение с Class<T> в качестве аргумента

Если подробнее остановиться на вашем подходе, это могло бы сработать, но это НЕКРАСИВО.

Некрасиво и не очень удобно. Понятия не имею, зачем вам это нужно, tbh.

 class Property<T> {

    public T value;
    private final Class<T> clazz;

    public Property(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }       

    @SuppressWarnings("unchecked")
    public void setValue(String input) {
        if (clazz.isAssignableFrom(String.class)) {
            value = (T) input;
        } else if (clazz.isAssignableFrom(Integer.class)) {
            value = (T) Integer.valueOf(input);
        } else if (clazz.isAssignableFrom(Boolean.class)) {
            value = (T) Boolean.valueOf(input);
        } else if (clazz.isAssignableFrom(Double.class)) {
            value = (T) Double.valueOf(input);
        } else {
            throw new IllegalArgumentException("Bad type.");
        }
    }
}
  

Используется примерно так:

 Property<String> ff = new Property<>(String.class);
ff.setValue("sdgf");

Property<Integer> sdg = new Property<>(Integer.class);
sdg.setValue("123");

System.out.println(ff.value);
System.out.println(sdg.value);
  

Решение с Reflection

По-видимому, можно определить параметр, используемый для создания экземпляра property .

Эта маленькая волшебная формула дает вам именно это:

 (Class<?>) getClass().getTypeParameters()[0].getBounds()[0]
  

Я даже не знаю, как мне удалось это найти. Ну, вот и все:

 class Property<T> {

    T value;    

    @SuppressWarnings("unchecked")
    public void setValue(String input)
    {
        // BEHOLD, MAGIC!
        Class<?> clazz = (Class<?>) getClass().getTypeParameters()[0].getBounds()[0];

        if (clazz.isAssignableFrom(String.class)) {
            value = (T) input;
        } else if (clazz.isAssignableFrom(Integer.class)) {
            value = (T) Integer.valueOf(input);
        } else if (clazz.isAssignableFrom(Boolean.class)) {
            value = (T) Boolean.valueOf(input);
        } else if (clazz.isAssignableFrom(Double.class)) {
            value = (T) Double.valueOf(input); 
        } else {
            throw new IllegalArgumentException("Bad type.");
        }
    }
}
  

И не смотрите на меня, я бы не стал это использовать. У меня есть некоторый здравый смысл.

Комментарии:

1. У меня был этот код раньше, но я пытался его избежать. Я думаю, что я проанализирую string на более высоком уровне, а затем вызову метод setValue (T input) с типом T. Но спасибо.

2. Вам пришлось бы передать класс T в качестве аргумента конструктору Property. Тогда вы могли бы сравнить этот класс с некоторыми известными типами и перейти к нему — но это НЕКРАСИВО. Полуморфизм здесь нужно использовать, а не избегать.

3. Что ж, действительно есть вариант использования man. Представьте, что простой текстовый конфигурационный файл, XML, результат веб-сервиса (что угодно) выдает вам сопоставление <string,строка>, и вам действительно нужен один универсальный метод, скажем <V> V GetValueByKey(string key) , который выдает вам подходящее значение для соответствующего ключа, но желаемого типа — вместо строки.

Ответ №2:

Я нашел новый способ, используя Spring.

 public static <T> T convertJsonToObject(String json, Class<T> valueType) {
    try {
        return json != null ? new ObjectMapper().readValue(json, valueType) : null;
    } catch (Exception e) {
        return null;
    }
}
  
 private static void processValidate(Class<?> valueClass, String value) {
     Object parsedValue = Utils.convertJsonToObject(value, valueClass);
     // logic
}
  

Ответ №3:

Другой способ, использующий отражение, — это

 public <T> T setValue(String value, Class<T> valueType) {

    try {
        return (T) valueType.getDeclaredMethod("valueOf", String.class).invoke(null, value);
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
}