java - 返回动态对象类型的通用方法

标签 java generics methods casting

可能是之前有人问过的一个问题,但像往常一样,当你提到通用这个词时,你会得到一千个解释类型删除的答案。我很久以前就经历过那个阶段,现在对泛型及其使用有了很多了解,但这种情况稍微微妙一些。

我有一个表示电子表格中数据单元格的容器,它实际上以两种格式存储数据:作为用于显示的字符串,但也以另一种格式,取决于数据(存储为对象)。单元格还包含一个在类型之间转换的转换器,并且还对类型进行有效性检查(例如,IntegerTransformer 检查字符串是否为有效整数,如果它返回一个 Integer 来存储,反之亦然)。

单元格本身没有键入,因为我希望能够更改格式(例如,将辅助格式更改为浮点数而不是整数,或更改为原始字符串)而不必使用新类型重建单元格对象。之前的尝试确实使用了泛型类型,但是一旦定义了就无法更改类型,编码变得非常庞大并且有很多反射。

问题是:如何以键入的方式从我的 Cell 中获取数据?我进行了试验,发现即使没有定义约束,也可以通过方法来使用泛型类型

public class Cell {
    private String stringVal;
    private Object valVal;
    private Transformer<?> trans;
    private Class<?> valClass;

    public String getStringVal(){
        return stringVal;
    }

    public boolean setStringVal(){
        //this not only set the value, but checks it with the transformer that it meets constraints and updates valVal too
    }

    public <T> T getValVal(){
        return (T) valVal;
        //This works, but I don't understand why
    }
}

让我失望的一点是:那是?它不能转换任何东西,没有 T 类型的输入限制它匹配任何东西,实际上它在任何地方都没有真正说什么。拥有一个返回类型的 Object 只会在任何地方给转换带来麻烦。

在我的测试中,我设置了一个 Double 值,它存储了 Double(作为一个对象),当我这样做时 Double testdou = testCell.getValVal();它立即起作用,甚至没有未经检查的 Actor 警告。但是,当我执行 String teststr = testCell.getValVal() 时,我收到了 ClassCastException。真的不足为奇。

对此我有两种看法:

一:使用未定义的 Cast 似乎只不过是在方法返回后将强制转换放在方法内部而不是外部的一种笨拙方式。从用户的角度来看,它非常简洁,但该方法的用户必须担心使用正确的调用:所有这些都是在运行时隐藏复杂的警告和检查,但似乎有效。

第二种观点是:我不喜欢这段代码:它不干净,它不是我通常为自己编写的那种代码质量。代码应该是正确的,而不仅仅是工作。错误应该被捕获和处理,并被预期,即使唯一的预期用户是我自己,接口(interface)也应该是万无一失的,我总是更喜欢灵活的通用和可重用的技术,而不是笨拙的技术。问题是:有没有正常的方法可以做到这一点?这是实现无类型,所有接受 ArrayList 的偷偷摸摸的方式,它返回任何你想要的东西而无需强制转换?或者我在这里遗漏了什么。有人告诉我我不应该相信这段代码!

也许比我想要的更像是一个哲学问题,但我想这就是我要问的。

编辑:进一步测试。

我尝试了以下两个有趣的片段:
public <T> T getTypedElem() {
    T output = (T) this.typedElem;
    System.out.println(output.getClass());
    return output;
}

public <T> T getTypedElem() {
    T output = null;
    try {
        output = (T) this.typedElem;
        System.out.println(output.getClass());
    } catch (ClassCastException e) {
        System.out.println("class cast caught");
        return null;
    }
    return output;
}

当将 double 分配给 typedElem 并尝试将其放入 String 时,我得到了一个异常,不是在强制转换上,而是在返回时,并且第二个代码段不保护。 getClass 的输出是 java.lang.Double,这表明它是从 typedElem 动态推断出来的,但是编译器级别的类型检查只是被强制排除在外。

作为辩论的注释:还有一个用于获取 valClass 的函数,这意味着可以在运行时进行可分配性检查。

编辑2:结果

在考虑了选项后,我采用了两种解决方案:一种是轻量级解决方案,但将函数注释为 @depreciated,其次是您将其传递给您想要尝试将其转换为的类的解决方案。这样就可以根据情况选择了。

最佳答案

您可以尝试键入 token :

public <T> T getValue(Class<T> cls) {
    if (valVal == null) return null;
    else {
        if (cls.isInstance(valVal)) return cls.cast(valVal);
        return null;
    }
}

请注意,这不会进行任何转换(即,如果 DoublevalValFloat 的实例,则不能使用此方法提取 Integer )。

顺便说一句,你应该得到关于 getValVal 定义的编译器警告。 .这是因为无法在运行时检查强制转换(Java 泛型通过“删除”工作,本质上意味着泛型类型参数在编译后会被遗忘),因此生成的代码更像是:
public Object getValVal() {
    return valVal;
}

关于java - 返回动态对象类型的通用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12569833/

相关文章:

python:将中间人注入(inject)方法调用

java - String 类型的 strip() 方法未定义

java - 在 Java 8 中对多个数组使用流/函数式编程

java - java "VM thread"是做什么的?

java - Map<A,B> foo = new HashMap<A,B>() 和 new HashMap<>() 的区别?

java - 需要帮助理解 java 中复杂的泛型声明

java - Activity 已泄漏窗口 com.android.internal.policy.impl.PhoneWindows$DecorView{42d9a800

java - getResourceAsStream 返回 HttpInputStream,而不是整个文件

java - 尝试打印到JTextArea

C# 将任何方法作为参数传递