可能是一个以前被问过的问题,但像往常一样,当你提到通用这个词时,你会得到一千个解释类型擦除的答案。我很久以前就经历过这个阶段,现在对泛型及其使用有了很多了解,但这种情况稍微微妙一些。
我有一个代表电子表格中数据单元格的容器,它实际上以两种格式存储数据:作为用于显示的字符串,但也以另一种格式,取决于数据(存储为对象)。该单元格还包含一个在类型之间进行转换的转换器,并且还对类型进行有效性检查(例如,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(); 时它立即奏效,甚至没有未经检查的演员警告。但是,当我执行 String teststr = testCell.getValVal() 时,我得到了 ClassCastException。确实不足为奇。
对此我看到有两种观点:
一:使用未定义的强制转换似乎只不过是一种将强制转换放在方法内部而不是返回后外部的方法。从用户的角度来看,它非常简洁,但是该方法的用户必须担心使用正确的调用:所有这些所做的都是隐藏复杂的警告和检查,直到运行时,但似乎有效。
第二种观点是:我不喜欢这段代码:它不干净,它不是我通常为自己编写的代码质量而自豪的。代码应该是正确的,而不仅仅是工作。错误应该被捕获和处理,并且预期,界面应该是万无一失的,即使唯一的预期用户是我自己,而且我总是更喜欢灵活的通用和可重用的技术,而不是尴尬的一次性技术。问题是:有什么正常的方法可以做到这一点吗?这是实现无类型、全部接受 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,第二种是您将要尝试将其转换为的类传递给它的解决方案。这样就根据情况来选择了。