类型转换和泛型

Typecasting and Generics

我正在开发一个消息解析库,其中消息正文在每种情况下都以字符串形式返回。我想创建一个函数,自动将该结果转换为正确的类型,但我不确定是否可行。

我的结构是这样的: 我有一个名为 ReturnType 的枚举。这包含这包含 return 类型的 class 变量。

我有一个函数映射,它将用户尝试调用的函数映射到 return 类型的消息响应。

我想我已经在某种程度上工作了,但问题是我必须将我想要的 class 包含在 getTypedValue() 方法调用中,我认为这是多余的,因为我已经知道 return 类型基于函数映射中的 ReturnType 变量。这是一些代码:

public enum ReturnType {
    NA(String.class),
    INTEGER(Integer.class),
    JSON(JsonElement.class),
    STRING(String.class),
    FLOAT(Float.class),
    DOUBLE(Double.class),
    IP_ADDRESS(InetAddress.class),
    UNKNOWN(String.class),
    BOOLEAN(Boolean.class);

    private Class<?> c;

    <T> ReturnType(Class<T> clazz) {
        this.c = clazz;
    }

    public Class<?> getClazz() {
        return this.c;
    }
}

这是我的 getTypedValue() 方法:

public <T> T getTypedValue(String functionName, String value, Class<T> clazz) {
        ReturnType type = this.getReturnType(functionName);
        //TODO: Finish this
        if(type.getClazz().equals(clazz)) {
            switch(type) {
                case DOUBLE:
                    break;
                case FLOAT:
                    break;
                case INTEGER:
                    return clazz.cast(Integer.parseInt(value));
                case IP_ADDRESS:
                    break;
                case JSON:
                    break;
                case STRING:
                    return clazz.cast(value);
                case UNKNOWN:
                    return clazz.cast(value);
                default:
                    return clazz.cast(value);
            }
            return null;
        }else {
            throw new UnsupportedOperationException(functionName +" does not support that class("+clazz.getSimpleName()+")");
        }
    }

以下是用户调用获取输入值的方式。为了简化,我输入了一个静态值 (15),但这实际上是一个从消息响应中获取结果的方法调用:

Integer volume = fm.getTypedValue("VOLUME", "15", Integer.class);

有没有办法在不提供 class 的情况下使其 return 成为正确的类型?即

Integer volume = fm.getTypedValue("VOLUME", "15");
Double minutes = fm.getTypedValue("MINUTES", "15.25");
Boolean isRunning = fm.getTypedValue("IS_RUNNING","true");

我不确定是否可行,即使可行,我认为我目前拥有的代码可能有点丑陋且难以理解。

遗憾的是,没有办法。编译器期望类型在运行时丢失,这使得将它分配给 Object

以外的任何东西都不安全

或者,您可以 return 一个 Object 并进行多次 instanceof 检查,但我认为那更糟。

另一种方法是使用提供的泛型调用方法。不幸的是,我不确定这是否会导致其他编译错误。试试

Double minutes = fm.<Double>getTypedValue("MINUTES", "15.25");

向泛型方法提供 Class 实例没有错,我在许多不同的库中都看到过这样的情况。

您可以做到这一点,但您将失去编译器为您提供的优势,即在编译时检查类型是否正确。

使用类似于 getTypedValue 的实现:

@SuppressWarnings({ "unchecked" })
public <T> T getTypedValue(String functionName, String value) {
    ReturnType type = this.getReturnType(functionName);
    switch(type) {
        case DOUBLE:
        case FLOAT:
        case JSON:
        case IP_ADDRESS:
            break;
        case INTEGER:
            return (T) Integer.valueOf(value);
        case STRING:
        case UNKNOWN:
        default:
            return (T) value;
    }
    return null;
}

您可以毫无问题地使用它:

int volume = fm.getTypedValue("VOLUME", "15");

但是,另一方面你也可以使用这个(编译器不会显示任何错误)

String volume = fm.getTypedValue("VOLUME", "15");

您将收到类型为 ClassCastException 的运行时异常,内容如下:

java.lang.Integer cannot be cast to java.lang.String


如果您想要可能 不太容易出错的东西(在编译时而不是运行时显示错误),您可以在 handle/know/provide 中定义所有函数class(枚举不处理泛型)。

public final class Function<T> {
    private final Class<T> type;
    private Function(Class<T> type) {
        this.type = type;
    }
    public Class<T> getType() {
        return type;
    }
    public static final Function<Integer> VOLUME = new Function<>(Integer.class);
    public static final Function<Boolean> IS_RUNNING = new Function<>(Boolean.class);
    public static final Function<String> NAME = new Function<>(String.class);
}

(您可以根据需要重构,也许添加 ReturnType。您可以在 Function 中添加一些自定义逻辑。也可以在构造函数参数,以防以后需要名称)

然后稍微改变一下getTypedValue方法:

public <T> T getTypedValue(Function<T> function, String value) {
    ReturnType returnType = ReturnType.getReturnTypeByClass(function.getType());
    // ... same as before
}

您可以根据收到的 class 创建方法 getReturnTypeByClass() 来获取 returnType

这会起作用:

int volume = test2.getTypedValue(Function.VOLUME, "123");

这将显示编译时错误,非常容易检测和修复:

String volume = test2.getTypedValue(Function.VOLUME, "123");

最后,您可以将 Function class 的定义和另一个 class 名称中的函数声明分开,例如 Functions.

如下修改枚举。

public enum ReturnType {
    NA(String.class) {
        @Override
        public String getValue(String param) {
            return param;
        }
    },
    INTEGER(Integer.class) {
        @Override
        public Integer getValue(String param) {
            return Integer.valueOf(param);
        }
    },
    JSON(JsonElement.class) {
        @Override
        public <T> T getValue(String param) {
            return null;
        }
    },
    STRING(String.class) {
        @Override
        public String getValue(String param) {
            return param;
        }
    },
    FLOAT(Float.class),
    DOUBLE(Double.class),
    IP_ADDRESS(InetAddress.class),
    UNKNOWN(String.class),
    BOOLEAN(Boolean.class);

    private Class<?> c;

    <T> ReturnType(Class<T> clazz) {
        this.c = clazz;
    }

    public Class<?> getClazz() {
        return this.c;
    }

    public abstract <T>  T getValue(String param);
}

您必须在每个 ReturnType 中实现 return 从 String 生成 T 值。

然后您可以使用 ReturnType 枚举,如下所示。

public <T> T getTypedValue(String functionName, String value, Class<T> clazz) {
        ReturnType type = this.getReturnType(functionName);
        if(type.getClazz().equals(clazz)) {
           return type.getValue(value);
        }
        return null;
}

请忽略编译器错误并修复它。我想给你一个想法来实现其他方式。