Java 返回动态对象类型的泛型方法

Java 返回动态对象类型的泛型方法,java,generics,methods,casting,Java,Generics,Methods,Casting,可能是以前问过的一个问题,但像往常一样,你一提到泛型这个词,就会得到一千个解释类型擦除的答案。我很久以前就经历过这个阶段,现在对泛型及其使用有了很多了解,但这种情况稍微微妙一些 我有一个容器,表示电子表格中的一个数据单元,它实际上以两种格式存储数据:一种是用于显示的字符串,另一种格式取决于数据(存储为对象)。该单元格还保存一个在类型之间转换的转换器,并对类型进行有效性检查(例如,IntegerTransformer检查字符串是否为有效整数,如果为,则返回要存储的整数,反之亦然) 单元格本身不是类

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

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

单元格本身不是类型化的,因为我希望能够更改格式(例如,将次要格式更改为浮点而不是整数,或更改为原始字符串),而无需使用新类型重新生成单元格对象。以前的一次尝试确实使用了泛型类型,但是一旦定义了类型,就无法更改该类型。由于大量的反射,编码变得非常庞大

问题是:如何以键入的方式从我的单元格中取出数据?我进行了实验,发现使用泛型类型可以通过方法完成,即使没有定义约束

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
    }
}
公共类单元格{
私有字符串stringVal;
私有对象瓣膜;
专用变压器变压器;
私人阶级;
公共字符串getStringVal(){
返回stringVal;
}
公共布尔集合字符串val(){
//这不仅可以设置值,还可以使用转换器检查值是否满足约束条件,并更新valVal
}
公共T getValVal(){
返回(T)瓣膜;
//这行得通,但我不明白为什么
}
}
让我不快的是:是吗?它不能铸造任何东西,没有类型t的输入限制它匹配任何东西,实际上它在任何地方都没有说任何东西。拥有一个返回类型的对象只会使铸造变得复杂

在我的测试中,我设置了一个Double值,它存储Double(作为一个对象),当我执行Double testdou=testCell.getValVal()时;它立即起作用,甚至没有未经检查的施法警告。然而,当我使用字符串teststr=testCell.getValVal()时,我得到了一个ClassCastException。毫不奇怪

我对此有两种看法:

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

第二种观点是:我不喜欢这段代码:它不干净,它不是我通常引以为豪的那种代码质量。代码应该是正确的,而不仅仅是工作。错误应该被捕获和处理,并且是可以预见的,接口应该是万无一失的,即使唯一的预期用户是我自己,我总是喜欢灵活的通用和可重用技术,而不是笨拙的技术。问题是:有什么正常的方法可以做到这一点吗?这是实现无类型、全接受ArrayList的一种偷偷摸摸的方法吗?该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;
}
public T getTypedElem(){
T输出=(T)this.typedElem;
System.out.println(output.getClass());
返回输出;
}
公共T getTypedElem(){
T输出=null;
试一试{
输出=(T)this.typedElem;
System.out.println(output.getClass());
}catch(ClassCastException e){
System.out.println(“类cast-catch”);
返回null;
}
返回输出;
}
当给typedElem分配一个double并试图将其放入一个字符串时,我得到一个异常,不是在cast to上,而是在return上,第二个代码段不受保护。getClass的输出是java.lang.Double,这表明它是从typedElem动态推断出来的,但编译器级别的类型检查只是被迫停止

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

编辑2:结果


在考虑了这些选项之后,我使用了两种解决方案:一种是轻量级解决方案,但将函数注释为@Distributed;另一种是将函数传递给您希望将其转换为的类的解决方案。这样,这是一个取决于情况的选择

您可以尝试键入标记:

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

我认为你是一个静态类型的人,但让我试试:你有没有想过在这方面使用groovy这样的动态语言

从你的描述来看,在我看来,类型更多的是阻碍而不是帮助任何事情

在groovy中,您可以让
Cell.valVal
为动态类型,并轻松实现以下转换:

class Cell {
  String val
  def valVal
}

def cell = new Cell(val:"10.0")
cell.valVal = cell.val as BigDecimal
BigDecimal valVal = cell.valVal
assert valVal.class == BigDecimal
assert valVal == 10.0

cell.val = "20"
cell.valVal = cell.val as Integer
Integer valVal2 = cell.valVal
assert valVal2.class == Integer
assert valVal2 == 20
其中,
as
是最常见的转换所需的一切。你也可以加上你的


如果需要转换其他代码块,请注意,java的语法是有效的groovy语法,除了
do{…}while()
block

,正如您所发现的,使用java的类型系统可以表达的内容是有限的,即使使用泛型也是如此。有时,某些值的类型之间存在关系,您希望使用类型声明来断言这些关系,但您不能(或者,您可以这样做,代价是过于复杂和冗长的代码)。我认为这篇文章中的示例代码(问答)很好地说明了这一点

在本例中,Java编译器
class Cell {
  String val
  def valVal
}

def cell = new Cell(val:"10.0")
cell.valVal = cell.val as BigDecimal
BigDecimal valVal = cell.valVal
assert valVal.class == BigDecimal
assert valVal == 10.0

cell.val = "20"
cell.valVal = cell.val as Integer
Integer valVal2 = cell.valVal
assert valVal2.class == Integer
assert valVal2 == 20
public <T> void setObject(Transformer<T> transformer, T object) {}