通用方法中需要的Java冗余转换

时间:2019-09-24 13:54:43

标签: java generics instanceof

我正在寻找一种使用通用函数优化代码库中某些内容的方法。有一个返回类型为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 个答案:

答案 0 :(得分:4)

首先,该语句在逻辑上是错误的

if(type.isInstance(String.class))

如果typeClass<String>,则isInstance正在检查参数是否为字符串实例。您传递的参数是一个类实例(特别是Class<String>)。

如果您愿意,

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

您的意思是

if(type == String.class)

但是,即使此逻辑错误已解决,您的代码仍将具有未经检查的强制转换警告。

您缺少的部分就在这里

  即使我们知道,如果键入   字符串作为参数传递,它将仅返回ArrayList<T>   像ArrayList<String>方法

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

这正是为什么这表现为警告而不是错误的原因。这是一个警告,因为您正在做的事情潜在地错误;它不是绝对错误,否则根本无法编译。在这种情况下,警告应作为提示您再次检查您所做的正确,然后您可以很高兴地将其取消。

forString()

最后-这可能是您所设计的示例的伪像-但所有这些方法都没有用。与直接调用@SuppressWarnings("unchecked") public static <T> ArrayList<T> function(Class<T> type){ if(type == String.class) return (ArrayList<T>) forString(); return forGeneric(type); } 相比,似乎没有任何优势。在运行时,实际实例是相同的,无论它来自3种方法中的哪一种。

答案 1 :(得分:0)

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

要真正摆脱编译器警告,我将返回一个新列表:

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个字节,因此,包含4,000个对象的列表将占用不超过一兆字节的RAM。