有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

泛型方法中需要Java冗余强制转换

我正在寻找一种方法,用泛型函数优化代码库中的某些内容。有一个返回类型为List<Object>的函数,其返回类型可能为List<SpecifiedType>

Bellow是该函数的最简版本,称为function。它采用一个参数类型,基于它调用相应的函数(这里限制为String)或泛型函数

public static ArrayList<String> forString(){
    ArrayList<String> res = new ArrayList<>();
    // Fetching and processing data specific to String
    return res;
}

public static <T> ArrayList<T> forGeneric(Class<T> type){
    ArrayList<T> res = new ArrayList<>();
    // Fetching data
    return res;
}

public static <T> ArrayList<T> function(Class<T> type){
    if(type == String.class)
        return (ArrayList<T>) forString();
    return forGeneric(type);
}

目标是像这样调用上面的函数:ArrayList<SomeType> someTypes = function(SomeType.class);

关于上述代码,我注意到两件事:

  1. 强制转换到ArrayList<T>是必需的,即使我们知道如果将类型String作为参数传递,它将返回ArrayList<String>,就像forString()方法一样

  2. 强制转换为ArrayList<T>会给出Unchecked cast警告,即使返回类型为ArrayList<String>

我的问题是,有没有更好的方法来做到这一点(最好不用演员阵容),如果没有,为什么


共 (2) 个答案

  1. # 1 楼答案

    运行时代码无法基于运行时信息返回不同的泛型类型,因为泛型是一种编译时机制

    为了避免编译器警告,我将返回一个新列表:

    public static <T> ArrayList<T> function(Class<T> type){
        ArrayList<?> result;
        if (type.equals(String.class)) {
            result = forString();
        } else {
            result = forGeneric(type);
        }
    
        ArrayList<T> typedResult = new ArrayList<>(result.size());
        for (Object item : result) {
            typedResult.add(type.cast(item));
        }
    
        return typedResult;
    }
    

    这可能看起来很浪费,但事实并非如此。新列表不是新对象的列表,只是新引用。一个引用非常小,可能不超过256字节,所以一个包含4000个对象的列表所占用的内存不会超过1兆字节

  2. # 2 楼答案

    首先,这种说法在逻辑上是错误的

    if(type.isInstance(String.class))
    

    如果typeClass<String>,那么isInstance正在检查参数是否是字符串实例。您传递的参数是一个类实例(具体地说,是一个Class<String>

    如果你愿意

    String.class.isInstance(String.class) == false
    

    你的意思是

    if(type == String.class)
    

    然而,即使解决了这个逻辑错误,代码仍然会有一个未经检查的强制转换警告

    你错过的部分就在这里

    Cast to ArrayList<T> is required even though we know that if type String is passed as a parameter it will return ArrayList<String> just like forString() method

    没错我们知道这一点。但是我们知道的和编译器知道的是两件不同的事情。编译器不够聪明,无法检查条件并意识到类型是正确的。它可以想象足够聪明,但事实并非如此

    这正是为什么这表现为警告而不是错误。这是一个警告,因为你所做的事可能是错误的;它肯定不是错的,否则它根本不会编译。在这种情况下,警告应该作为一个提示,让你再次检查你所做的是正确的,然后你可以愉快地抑制它

    @SuppressWarnings("unchecked")
    public static <T> ArrayList<T> function(Class<T> type){
        if(type == String.class)
            return (ArrayList<T>) forString();
        return forGeneric(type);
    }
    

    最后,它可能是你精心设计的例子的产物,但所有这些方法都是无用的。与直接调用new ArrayList<>()相比,似乎没有任何优势。在运行时,实际实例是相同的,无论它来自3种方法中的哪一种