Java 泛型方法需要冗余转换
Java redundant casts required in generic method
我正在寻找一种方法来优化我们代码库中的一些东西,使用通用函数。有一个 return 类型 List<Object>
的函数可以 return 类型 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);
关于上面的代码,我注意到两件事:
Cast to ArrayList<T>
是必需的,即使我们知道如果类型 String
作为参数传递,它将 return ArrayList<String>
就像forString()
方法
转换为 ArrayList<T>
给出 Unchecked cast
警告,即使 return 类型将是 ArrayList<String>
我的问题是有没有更好的方法(最好没有强制转换),如果没有,那为什么
首先,这个说法在逻辑上是错误的
if(type.isInstance(String.class))
如果 type
是 Class<String>
,则 isInstance
正在检查参数是否为字符串实例。您传递的参数是一个 class 实例(具体来说,是一个 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 种方法中的哪一种,实际实例都是相同的。
运行时代码无法根据运行时信息 return 不同的泛型类型,因为泛型是一种编译时机制。
要真正摆脱编译器警告,我会return一个新列表:
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;
}
这可能看起来很浪费,但事实并非如此。 new List 不是新对象的列表,只是新的引用。引用非常小,可能不超过 256 字节,因此 4,000 个对象的列表占用的 RAM 不超过 1 兆字节。
我正在寻找一种方法来优化我们代码库中的一些东西,使用通用函数。有一个 return 类型 List<Object>
的函数可以 return 类型 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);
关于上面的代码,我注意到两件事:
Cast to
ArrayList<T>
是必需的,即使我们知道如果类型String
作为参数传递,它将 returnArrayList<String>
就像forString()
方法转换为
ArrayList<T>
给出Unchecked cast
警告,即使 return 类型将是ArrayList<String>
我的问题是有没有更好的方法(最好没有强制转换),如果没有,那为什么
首先,这个说法在逻辑上是错误的
if(type.isInstance(String.class))
如果 type
是 Class<String>
,则 isInstance
正在检查参数是否为字符串实例。您传递的参数是一个 class 实例(具体来说,是一个 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 returnArrayList<String>
just likeforString()
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 种方法中的哪一种,实际实例都是相同的。
运行时代码无法根据运行时信息 return 不同的泛型类型,因为泛型是一种编译时机制。
要真正摆脱编译器警告,我会return一个新列表:
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;
}
这可能看起来很浪费,但事实并非如此。 new List 不是新对象的列表,只是新的引用。引用非常小,可能不超过 256 字节,因此 4,000 个对象的列表占用的 RAM 不超过 1 兆字节。